Version 3.6.4
Fixed d8's broken readline history.
Removed the need for code delete events in CPU profiler (Issue 1466).
Fixed debugger stepping next with trycatch recursion (Issue 1639).
Fixing parallel execution in d8 (with -p) and some memory leaks.
Support for precise stepping in functions compiled before debugging was started (step 1).
git-svn-id: http://v8.googlecode.com/svn/trunk@9288 ce2b1a6d-e550-0410-aec6-3dcde31c8c00
diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc
index a35380c..60d2081 100644
--- a/src/arm/builtins-arm.cc
+++ b/src/arm/builtins-arm.cc
@@ -1230,16 +1230,17 @@
// 2. Get the function to call (passed as receiver) from the stack, check
// if it is a function.
// r0: actual number of arguments
- Label non_function;
+ Label slow, non_function;
__ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
__ JumpIfSmi(r1, &non_function);
__ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
- __ b(ne, &non_function);
+ __ b(ne, &slow);
// 3a. Patch the first argument if necessary when calling a function.
// r0: actual number of arguments
// r1: function
Label shift_arguments;
+ __ mov(r4, Operand(0, RelocInfo::NONE)); // indicate regular JS_FUNCTION
{ Label convert_to_object, use_global_receiver, patch_receiver;
// Change context eagerly in case we need the global receiver.
__ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
@@ -1286,8 +1287,9 @@
__ pop(r0);
__ mov(r0, Operand(r0, ASR, kSmiTagSize));
__ LeaveInternalFrame();
- // Restore the function to r1.
+ // Restore the function to r1, and the flag to r4.
__ ldr(r1, MemOperand(sp, r0, LSL, kPointerSizeLog2));
+ __ mov(r4, Operand(0, RelocInfo::NONE));
__ jmp(&patch_receiver);
// Use the global receiver object from the called function as the
@@ -1307,23 +1309,30 @@
__ jmp(&shift_arguments);
}
- // 3b. Patch the first argument when calling a non-function. The
+ // 3b. Check for function proxy.
+ __ bind(&slow);
+ __ mov(r4, Operand(1, RelocInfo::NONE)); // indicate function proxy
+ __ cmp(r2, Operand(JS_FUNCTION_PROXY_TYPE));
+ __ b(eq, &shift_arguments);
+ __ bind(&non_function);
+ __ mov(r4, Operand(2, RelocInfo::NONE)); // indicate non-function
+
+ // 3c. Patch the first argument when calling a non-function. The
// CALL_NON_FUNCTION builtin expects the non-function callee as
// receiver, so overwrite the first argument which will ultimately
// become the receiver.
// r0: actual number of arguments
// r1: function
- __ bind(&non_function);
+ // r4: call type (0: JS function, 1: function proxy, 2: non-function)
__ add(r2, sp, Operand(r0, LSL, kPointerSizeLog2));
__ str(r1, MemOperand(r2, -kPointerSize));
- // Clear r1 to indicate a non-function being called.
- __ mov(r1, Operand(0, RelocInfo::NONE));
// 4. Shift arguments and return address one slot down on the stack
// (overwriting the original receiver). Adjust argument count to make
// the original first argument the new receiver.
// r0: actual number of arguments
// r1: function
+ // r4: call type (0: JS function, 1: function proxy, 2: non-function)
__ bind(&shift_arguments);
{ Label loop;
// Calculate the copy start address (destination). Copy end address is sp.
@@ -1341,16 +1350,28 @@
__ pop();
}
- // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin,
+ // or a function proxy via CALL_FUNCTION_PROXY.
// r0: actual number of arguments
// r1: function
- { Label function;
- __ tst(r1, r1);
- __ b(ne, &function);
+ // r4: call type (0: JS function, 1: function proxy, 2: non-function)
+ { Label function, non_proxy;
+ __ tst(r4, r4);
+ __ b(eq, &function);
// Expected number of arguments is 0 for CALL_NON_FUNCTION.
__ mov(r2, Operand(0, RelocInfo::NONE));
- __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
__ SetCallKind(r5, CALL_AS_METHOD);
+ __ cmp(r4, Operand(1));
+ __ b(ne, &non_proxy);
+
+ __ push(r1); // re-add proxy object as additional argument
+ __ add(r0, r0, Operand(1));
+ __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY);
+ __ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+
+ __ bind(&non_proxy);
+ __ GetBuiltinEntry(r3, Builtins::CALL_NON_FUNCTION);
__ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET);
__ bind(&function);
@@ -1393,7 +1414,7 @@
__ push(r0);
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
- // Check the stack for overflow. We are not trying need to catch
+ // Check the stack for overflow. We are not trying to catch
// interruptions (e.g. debug break and preemption) here, so the "real stack
// limit" is checked.
Label okay;
@@ -1418,18 +1439,24 @@
__ mov(r1, Operand(0, RelocInfo::NONE)); // initial index
__ push(r1);
- // Change context eagerly to get the right global object if necessary.
- __ ldr(r0, MemOperand(fp, kFunctionOffset));
- __ ldr(cp, FieldMemOperand(r0, JSFunction::kContextOffset));
- // Load the shared function info while the function is still in r0.
- __ ldr(r1, FieldMemOperand(r0, JSFunction::kSharedFunctionInfoOffset));
-
- // Compute the receiver.
- Label call_to_object, use_global_receiver, push_receiver;
+ // Get the receiver.
__ ldr(r0, MemOperand(fp, kRecvOffset));
+ // Check that the function is a JS function (otherwise it must be a proxy).
+ Label push_receiver;
+ __ ldr(r1, MemOperand(fp, kFunctionOffset));
+ __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
+ __ b(ne, &push_receiver);
+
+ // Change context eagerly to get the right global object if necessary.
+ __ ldr(cp, FieldMemOperand(r1, JSFunction::kContextOffset));
+ // Load the shared function info while the function is still in r1.
+ __ ldr(r2, FieldMemOperand(r1, JSFunction::kSharedFunctionInfoOffset));
+
+ // Compute the receiver.
// Do not transform the receiver for strict mode functions.
- __ ldr(r2, FieldMemOperand(r1, SharedFunctionInfo::kCompilerHintsOffset));
+ Label call_to_object, use_global_receiver;
+ __ ldr(r2, FieldMemOperand(r2, SharedFunctionInfo::kCompilerHintsOffset));
__ tst(r2, Operand(1 << (SharedFunctionInfo::kStrictModeFunction +
kSmiTagSize)));
__ b(ne, &push_receiver);
@@ -1504,9 +1531,12 @@
__ b(ne, &loop);
// Invoke the function.
+ Label call_proxy;
ParameterCount actual(r0);
__ mov(r0, Operand(r0, ASR, kSmiTagSize));
__ ldr(r1, MemOperand(fp, kFunctionOffset));
+ __ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
+ __ b(ne, &call_proxy);
__ InvokeFunction(r1, actual, CALL_FUNCTION,
NullCallWrapper(), CALL_AS_METHOD);
@@ -1514,6 +1544,20 @@
__ LeaveInternalFrame();
__ add(sp, sp, Operand(3 * kPointerSize));
__ Jump(lr);
+
+ // Invoke the function proxy.
+ __ bind(&call_proxy);
+ __ push(r1); // add function proxy as last argument
+ __ add(r0, r0, Operand(1));
+ __ mov(r2, Operand(0, RelocInfo::NONE));
+ __ SetCallKind(r5, CALL_AS_METHOD);
+ __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY);
+ __ Call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+
+ __ LeaveInternalFrame();
+ __ add(sp, sp, Operand(3 * kPointerSize));
+ __ Jump(lr);
}
diff --git a/src/arm/code-stubs-arm.cc b/src/arm/code-stubs-arm.cc
index 7fd2fe6..e65f6d9 100644
--- a/src/arm/code-stubs-arm.cc
+++ b/src/arm/code-stubs-arm.cc
@@ -4713,7 +4713,7 @@
void CallFunctionStub::Generate(MacroAssembler* masm) {
- Label slow;
+ Label slow, non_function;
// The receiver might implicitly be the global object. This is
// indicated by passing the hole as the receiver to the call
@@ -4739,7 +4739,7 @@
// Check that the function is really a JavaScript function.
// r1: pushed function (to be verified)
- __ JumpIfSmi(r1, &slow);
+ __ JumpIfSmi(r1, &non_function);
// Get the map of the function object.
__ CompareObjectType(r1, r2, r2, JS_FUNCTION_TYPE);
__ b(ne, &slow);
@@ -4767,8 +4767,23 @@
// Slow-case: Non-function called.
__ bind(&slow);
+ // Check for function proxy.
+ __ cmp(r2, Operand(JS_FUNCTION_PROXY_TYPE));
+ __ b(ne, &non_function);
+ __ push(r1); // put proxy as additional argument
+ __ mov(r0, Operand(argc_ + 1, RelocInfo::NONE));
+ __ mov(r2, Operand(0, RelocInfo::NONE));
+ __ GetBuiltinEntry(r3, Builtins::CALL_FUNCTION_PROXY);
+ __ SetCallKind(r5, CALL_AS_FUNCTION);
+ {
+ Handle<Code> adaptor =
+ masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
+ __ Jump(adaptor, RelocInfo::CODE_TARGET);
+ }
+
// CALL_NON_FUNCTION expects the non-function callee as receiver (instead
// of the original receiver from the call site).
+ __ bind(&non_function);
__ str(r1, MemOperand(sp, argc_ * kPointerSize));
__ mov(r0, Operand(argc_)); // Setup the number of arguments.
__ mov(r2, Operand(0, RelocInfo::NONE));
diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc
index 4d27a8d..50ed8b1 100644
--- a/src/arm/full-codegen-arm.cc
+++ b/src/arm/full-codegen-arm.cc
@@ -2028,9 +2028,8 @@
// Record source position for debugger.
SetSourcePosition(expr->position());
// Call the IC initialization code.
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arg_count, in_loop, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
__ Call(ic, mode, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
@@ -2061,9 +2060,8 @@
// Record source position for debugger.
SetSourcePosition(expr->position());
// Call the IC initialization code.
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count, in_loop);
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count);
__ ldr(r2, MemOperand(sp, (arg_count + 1) * kPointerSize)); // Key.
__ Call(ic, RelocInfo::CODE_TARGET, expr->id());
RecordJSReturnSite(expr);
@@ -2084,8 +2082,7 @@
}
// Record source position for debugger.
SetSourcePosition(expr->position());
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, flags);
+ CallFunctionStub stub(arg_count, flags);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -2184,8 +2181,7 @@
// Record source position for debugger.
SetSourcePosition(expr->position());
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_IMPLICIT);
+ CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -2514,7 +2510,7 @@
&if_true, &if_false, &fall_through);
__ JumpIfSmi(r0, if_false);
- __ CompareObjectType(r0, r1, r1, JS_FUNCTION_TYPE);
+ __ CompareObjectType(r0, r1, r2, JS_FUNCTION_TYPE);
PrepareForBailoutBeforeSplit(TOS_REG, true, if_true, if_false);
Split(eq, if_true, if_false, fall_through);
@@ -3553,9 +3549,7 @@
__ mov(r2, Operand(expr->name()));
RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arg_count,
- NOT_IN_LOOP,
- mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
__ Call(ic, mode, expr->id());
// Restore context register.
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc
index 0bb4d4b..2e49cae 100644
--- a/src/arm/ic-arm.cc
+++ b/src/arm/ic-arm.cc
@@ -393,7 +393,6 @@
// Probe the stub cache.
Code::Flags flags = Code::ComputeFlags(kind,
- NOT_IN_LOOP,
MONOMORPHIC,
extra_ic_state,
NORMAL,
@@ -734,9 +733,8 @@
// -----------------------------------
// Probe the stub cache.
- Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC,
- NOT_IN_LOOP,
- MONOMORPHIC);
+ Code::Flags flags =
+ Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC);
Isolate::Current()->stub_cache()->GenerateProbe(
masm, flags, r0, r2, r3, r4, r5);
@@ -1380,10 +1378,8 @@
// -----------------------------------
// Get the receiver from the stack and probe the stub cache.
- Code::Flags flags = Code::ComputeFlags(Code::STORE_IC,
- NOT_IN_LOOP,
- MONOMORPHIC,
- strict_mode);
+ Code::Flags flags =
+ Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC, strict_mode);
Isolate::Current()->stub_cache()->GenerateProbe(
masm, flags, r1, r2, r3, r4, r5);
diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc
index 9e67da1..f5d7449 100644
--- a/src/arm/lithium-codegen-arm.cc
+++ b/src/arm/lithium-codegen-arm.cc
@@ -3178,7 +3178,7 @@
int arity = instr->arity();
Handle<Code> ic =
- isolate()->stub_cache()->ComputeKeyedCallInitialize(arity, NOT_IN_LOOP);
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arity);
CallCode(ic, RelocInfo::CODE_TARGET, instr);
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
}
@@ -3190,7 +3190,7 @@
int arity = instr->arity();
RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
__ mov(r2, Operand(instr->name()));
CallCode(ic, mode, instr);
// Restore context register.
@@ -3202,7 +3202,7 @@
ASSERT(ToRegister(instr->result()).is(r0));
int arity = instr->arity();
- CallFunctionStub stub(arity, NOT_IN_LOOP, RECEIVER_MIGHT_BE_IMPLICIT);
+ CallFunctionStub stub(arity, RECEIVER_MIGHT_BE_IMPLICIT);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
__ Drop(1);
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
@@ -3215,7 +3215,7 @@
int arity = instr->arity();
RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
__ mov(r2, Operand(instr->name()));
CallCode(ic, mode, instr);
__ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h
index 9d66359..6084fde 100644
--- a/src/arm/macro-assembler-arm.h
+++ b/src/arm/macro-assembler-arm.h
@@ -596,9 +596,7 @@
// Compare instance type in a map. map contains a valid map object whose
// object type should be compared with the given type. This both
- // sets the flags and leaves the object type in the type_reg register. It
- // leaves the heap object in the heap_object register unless the heap_object
- // register is the same register as type_reg.
+ // sets the flags and leaves the object type in the type_reg register.
void CompareInstanceType(Register map,
Register type_reg,
InstanceType type);
diff --git a/src/array.js b/src/array.js
index 32a370f..4dd23c8 100644
--- a/src/array.js
+++ b/src/array.js
@@ -208,7 +208,7 @@
// Call ToString if toLocaleString is not a function.
// See issue 877615.
var e_obj = ToObject(e);
- if (IS_FUNCTION(e_obj.toLocaleString))
+ if (IS_SPEC_FUNCTION(e_obj.toLocaleString))
return ToString(e_obj.toLocaleString());
else
return ToString(e);
@@ -730,7 +730,7 @@
// In-place QuickSort algorithm.
// For short (length <= 22) arrays, insertion sort is used for efficiency.
- if (!IS_FUNCTION(comparefn)) {
+ if (!IS_SPEC_FUNCTION(comparefn)) {
comparefn = function (x, y) {
if (x === y) return 0;
if (%_IsSmi(x) && %_IsSmi(y)) {
@@ -993,7 +993,7 @@
["Array.prototype.filter"]);
}
- if (!IS_FUNCTION(f)) {
+ if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
if (IS_NULL_OR_UNDEFINED(receiver)) {
@@ -1022,7 +1022,7 @@
["Array.prototype.forEach"]);
}
- if (!IS_FUNCTION(f)) {
+ if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
if (IS_NULL_OR_UNDEFINED(receiver)) {
@@ -1048,7 +1048,7 @@
["Array.prototype.some"]);
}
- if (!IS_FUNCTION(f)) {
+ if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
if (IS_NULL_OR_UNDEFINED(receiver)) {
@@ -1073,7 +1073,7 @@
["Array.prototype.every"]);
}
- if (!IS_FUNCTION(f)) {
+ if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
if (IS_NULL_OR_UNDEFINED(receiver)) {
@@ -1097,7 +1097,7 @@
["Array.prototype.map"]);
}
- if (!IS_FUNCTION(f)) {
+ if (!IS_SPEC_FUNCTION(f)) {
throw MakeTypeError('called_non_callable', [ f ]);
}
if (IS_NULL_OR_UNDEFINED(receiver)) {
@@ -1245,7 +1245,7 @@
["Array.prototype.reduce"]);
}
- if (!IS_FUNCTION(callback)) {
+ if (!IS_SPEC_FUNCTION(callback)) {
throw MakeTypeError('called_non_callable', [callback]);
}
@@ -1281,7 +1281,7 @@
["Array.prototype.reduceRight"]);
}
- if (!IS_FUNCTION(callback)) {
+ if (!IS_SPEC_FUNCTION(callback)) {
throw MakeTypeError('called_non_callable', [callback]);
}
var i = ToUint32(this.length) - 1;
diff --git a/src/builtins.cc b/src/builtins.cc
index d403a95..e6a0699 100644
--- a/src/builtins.cc
+++ b/src/builtins.cc
@@ -1583,7 +1583,6 @@
functions->s_name = #aname; \
functions->name = k##aname; \
functions->flags = Code::ComputeFlags(Code::kind, \
- NOT_IN_LOOP, \
state, \
extra); \
functions->extra_args = NO_EXTRA_ARGUMENTS; \
diff --git a/src/builtins.h b/src/builtins.h
index f9a5a13..31090d3 100644
--- a/src/builtins.h
+++ b/src/builtins.h
@@ -238,6 +238,8 @@
V(FILTER_KEY, 1) \
V(CALL_NON_FUNCTION, 0) \
V(CALL_NON_FUNCTION_AS_CONSTRUCTOR, 0) \
+ V(CALL_FUNCTION_PROXY, 1) \
+ V(CALL_FUNCTION_PROXY_AS_CONSTRUCTOR, 1) \
V(TO_OBJECT, 0) \
V(TO_NUMBER, 0) \
V(TO_STRING, 0) \
diff --git a/src/code-stubs.cc b/src/code-stubs.cc
index 9c87c84..00da4cb 100644
--- a/src/code-stubs.cc
+++ b/src/code-stubs.cc
@@ -114,7 +114,6 @@
// Copy the generated code into a heap object.
Code::Flags flags = Code::ComputeFlags(
static_cast<Code::Kind>(GetCodeKind()),
- InLoop(),
GetICState());
Handle<Code> new_object = factory->NewCode(
desc, flags, masm.CodeObject(), NeedsImmovableCode());
@@ -152,7 +151,6 @@
// Try to copy the generated code into a heap object.
Code::Flags flags = Code::ComputeFlags(
static_cast<Code::Kind>(GetCodeKind()),
- InLoop(),
GetICState());
Object* new_object;
{ MaybeObject* maybe_new_object =
@@ -316,17 +314,12 @@
void CallFunctionStub::PrintName(StringStream* stream) {
- const char* in_loop_name = NULL; // Make g++ happy.
- switch (in_loop_) {
- case NOT_IN_LOOP: in_loop_name = ""; break;
- case IN_LOOP: in_loop_name = "_InLoop"; break;
- }
const char* flags_name = NULL; // Make g++ happy.
switch (flags_) {
case NO_CALL_FUNCTION_FLAGS: flags_name = ""; break;
case RECEIVER_MIGHT_BE_IMPLICIT: flags_name = "_Implicit"; break;
}
- stream->Add("CallFunctionStub_Args%d%s%s", argc_, in_loop_name, flags_name);
+ stream->Add("CallFunctionStub_Args%d%s", argc_, flags_name);
}
diff --git a/src/code-stubs.h b/src/code-stubs.h
index 726533c..64c89b9 100644
--- a/src/code-stubs.h
+++ b/src/code-stubs.h
@@ -168,10 +168,6 @@
virtual Major MajorKey() = 0;
virtual int MinorKey() = 0;
- // The CallFunctionStub needs to override this so it can encode whether a
- // lazily generated function should be fully optimized or not.
- virtual InLoopFlag InLoop() { return NOT_IN_LOOP; }
-
// BinaryOpStub needs to override this.
virtual int GetCodeKind();
@@ -646,8 +642,8 @@
class CallFunctionStub: public CodeStub {
public:
- CallFunctionStub(int argc, InLoopFlag in_loop, CallFunctionFlags flags)
- : argc_(argc), in_loop_(in_loop), flags_(flags) { }
+ CallFunctionStub(int argc, CallFunctionFlags flags)
+ : argc_(argc), flags_(flags) { }
void Generate(MacroAssembler* masm);
@@ -657,26 +653,20 @@
private:
int argc_;
- InLoopFlag in_loop_;
CallFunctionFlags flags_;
virtual void PrintName(StringStream* stream);
// Minor key encoding in 32 bits with Bitfield <Type, shift, size>.
- class InLoopBits: public BitField<InLoopFlag, 0, 1> {};
- class FlagBits: public BitField<CallFunctionFlags, 1, 1> {};
- class ArgcBits: public BitField<int, 2, 32 - 2> {};
+ class FlagBits: public BitField<CallFunctionFlags, 0, 1> {};
+ class ArgcBits: public BitField<unsigned, 1, 32 - 1> {};
Major MajorKey() { return CallFunction; }
int MinorKey() {
// Encode the parameters in a unique 32 bit value.
- return InLoopBits::encode(in_loop_)
- | FlagBits::encode(flags_)
- | ArgcBits::encode(argc_);
+ return FlagBits::encode(flags_) | ArgcBits::encode(argc_);
}
- InLoopFlag InLoop() { return in_loop_; }
-
bool ReceiverMightBeImplicit() {
return (flags_ & RECEIVER_MIGHT_BE_IMPLICIT) != 0;
}
diff --git a/src/cpu-profiler-inl.h b/src/cpu-profiler-inl.h
index 938b632..4982197 100644
--- a/src/cpu-profiler-inl.h
+++ b/src/cpu-profiler-inl.h
@@ -51,11 +51,6 @@
}
-void CodeDeleteEventRecord::UpdateCodeMap(CodeMap* code_map) {
- code_map->DeleteCode(start);
-}
-
-
void SharedFunctionInfoMoveEventRecord::UpdateCodeMap(CodeMap* code_map) {
code_map->MoveCode(from, to);
}
diff --git a/src/cpu-profiler.cc b/src/cpu-profiler.cc
index bb480fc..6549028 100644
--- a/src/cpu-profiler.cc
+++ b/src/cpu-profiler.cc
@@ -137,16 +137,6 @@
}
-void ProfilerEventsProcessor::CodeDeleteEvent(Address from) {
- CodeEventsContainer evt_rec;
- CodeDeleteEventRecord* rec = &evt_rec.CodeDeleteEventRecord_;
- rec->type = CodeEventRecord::CODE_DELETE;
- rec->order = ++enqueue_order_;
- rec->start = from;
- events_buffer_.Enqueue(evt_rec);
-}
-
-
void ProfilerEventsProcessor::SharedFunctionInfoMoveEvent(Address from,
Address to) {
CodeEventsContainer evt_rec;
@@ -425,7 +415,6 @@
void CpuProfiler::CodeDeleteEvent(Address from) {
- Isolate::Current()->cpu_profiler()->processor_->CodeDeleteEvent(from);
}
diff --git a/src/cpu-profiler.h b/src/cpu-profiler.h
index 4175e8f..a71c0e0 100644
--- a/src/cpu-profiler.h
+++ b/src/cpu-profiler.h
@@ -48,7 +48,6 @@
#define CODE_EVENTS_TYPE_LIST(V) \
V(CODE_CREATION, CodeCreateEventRecord) \
V(CODE_MOVE, CodeMoveEventRecord) \
- V(CODE_DELETE, CodeDeleteEventRecord) \
V(SHARED_FUNC_MOVE, SharedFunctionInfoMoveEventRecord)
@@ -87,14 +86,6 @@
};
-class CodeDeleteEventRecord : public CodeEventRecord {
- public:
- Address start;
-
- INLINE(void UpdateCodeMap(CodeMap* code_map));
-};
-
-
class SharedFunctionInfoMoveEventRecord : public CodeEventRecord {
public:
Address from;
diff --git a/src/d8-readline.cc b/src/d8-readline.cc
index 8422783..71be933 100644
--- a/src/d8-readline.cc
+++ b/src/d8-readline.cc
@@ -1,4 +1,4 @@
-// Copyright 2008 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -72,6 +72,7 @@
rl_completer_word_break_characters = kWordBreakCharacters;
rl_bind_key('\t', rl_complete);
using_history();
+ stifle_history(Shell::kMaxHistoryEntries);
return read_history(Shell::kHistoryFileName) == 0;
}
@@ -88,6 +89,18 @@
void ReadLineEditor::AddHistory(const char* str) {
+ // Do not record empty input.
+ if (strlen(str) == 0) return;
+ // Remove duplicate history entry.
+ history_set_pos(history_length-1);
+ if (current_history()) {
+ do {
+ if (strcmp(current_history()->line, str) == 0) {
+ remove_history(where_history());
+ break;
+ }
+ } while (previous_history());
+ }
add_history(str);
}
diff --git a/src/d8.cc b/src/d8.cc
index 544e824..55f0d4c 100644
--- a/src/d8.cc
+++ b/src/d8.cc
@@ -70,6 +70,7 @@
#ifndef V8_SHARED
LineEditor *LineEditor::first_ = NULL;
const char* Shell::kHistoryFileName = ".d8_history";
+const int Shell::kMaxHistoryEntries = 1000;
LineEditor::LineEditor(Type type, const char* name)
@@ -117,6 +118,7 @@
CounterCollection* Shell::counters_ = &local_counters_;
i::Mutex* Shell::context_mutex_(i::OS::CreateMutex());
Persistent<Context> Shell::utility_context_;
+LineEditor* Shell::console = NULL;
#endif // V8_SHARED
Persistent<Context> Shell::evaluation_context_;
@@ -203,7 +205,7 @@
int n = static_cast<int>(fwrite(*str, sizeof(**str), str.length(), stdout));
if (n != str.length()) {
printf("Error in fwrite\n");
- exit(1);
+ Exit(1);
}
}
return Undefined();
@@ -521,7 +523,7 @@
NULL : counters_file_->memory();
if (memory == NULL) {
printf("Could not map counters file %s\n", name);
- exit(1);
+ Exit(1);
}
counters_ = static_cast<CounterCollection*>(memory);
V8::SetCounterFunction(LookupCounter);
@@ -718,7 +720,7 @@
int bz2_result = startup_data_decompressor.Decompress();
if (bz2_result != BZ_OK) {
fprintf(stderr, "bzip error code: %d\n", bz2_result);
- exit(1);
+ Exit(1);
}
#endif
@@ -780,8 +782,18 @@
}
+void Shell::Exit(int exit_code) {
+ // Use _exit instead of exit to avoid races between isolate
+ // threads and static destructors.
+ fflush(stdout);
+ fflush(stderr);
+ _exit(exit_code);
+}
+
+
#ifndef V8_SHARED
void Shell::OnExit() {
+ if (console != NULL) console->Close();
if (i::FLAG_dump_counters) {
printf("+----------------------------------------+-------------+\n");
printf("| Name | Value |\n");
@@ -886,20 +898,19 @@
HandleScope outer_scope;
Handle<String> name = String::New("(d8)");
#ifndef V8_SHARED
- LineEditor* editor = LineEditor::Get();
- printf("V8 version %s [console: %s]\n", V8::GetVersion(), editor->name());
+ console = LineEditor::Get();
+ printf("V8 version %s [console: %s]\n", V8::GetVersion(), console->name());
if (i::FLAG_debugger) {
printf("JavaScript debugger enabled\n");
}
- editor->Open();
+ console->Open();
while (true) {
- i::SmartArrayPointer<char> input = editor->Prompt(Shell::kPrompt);
+ i::SmartArrayPointer<char> input = console->Prompt(Shell::kPrompt);
if (input.is_empty()) break;
- editor->AddHistory(*input);
+ console->AddHistory(*input);
HandleScope inner_scope;
ExecuteString(String::New(*input), name, true, true);
}
- editor->Close();
#else
printf("V8 version %s [D8 light using shared library]\n", V8::GetVersion());
static const int kBufferSize = 256;
@@ -918,18 +929,24 @@
#ifndef V8_SHARED
class ShellThread : public i::Thread {
public:
- ShellThread(int no, i::Vector<const char> files)
+ // Takes ownership of the underlying char array of |files|.
+ ShellThread(int no, char* files)
: Thread("d8:ShellThread"),
no_(no), files_(files) { }
+
+ ~ShellThread() {
+ delete[] files_;
+ }
+
virtual void Run();
private:
int no_;
- i::Vector<const char> files_;
+ char* files_;
};
void ShellThread::Run() {
- char* ptr = const_cast<char*>(files_.start());
+ char* ptr = files_;
while ((ptr != NULL) && (*ptr != '\0')) {
// For each newline-separated line.
char* next_line = ReadLine(ptr);
@@ -942,23 +959,24 @@
// Prepare the context for this thread.
Locker locker;
- HandleScope scope;
+ HandleScope outer_scope;
Persistent<Context> thread_context = Shell::CreateEvaluationContext();
Context::Scope context_scope(thread_context);
while ((ptr != NULL) && (*ptr != '\0')) {
+ HandleScope inner_scope;
char* filename = ptr;
ptr = ReadWord(ptr);
// Skip empty strings.
if (strlen(filename) == 0) {
- break;
+ continue;
}
Handle<String> str = Shell::ReadFile(filename);
if (str.IsEmpty()) {
- printf("WARNING: %s not found\n", filename);
- break;
+ printf("File '%s' not found\n", filename);
+ Shell::Exit(1);
}
Shell::ExecuteString(str, String::New(filename), false, false);
@@ -983,15 +1001,6 @@
}
-void SourceGroup::ExitShell(int exit_code) {
- // Use _exit instead of exit to avoid races between isolate
- // threads and static destructors.
- fflush(stdout);
- fflush(stderr);
- _exit(exit_code);
-}
-
-
void SourceGroup::Execute() {
for (int i = begin_offset_; i < end_offset_; ++i) {
const char* arg = argv_[i];
@@ -1001,8 +1010,7 @@
Handle<String> file_name = String::New("unnamed");
Handle<String> source = String::New(argv_[i + 1]);
if (!Shell::ExecuteString(source, file_name, false, true)) {
- ExitShell(1);
- return;
+ Shell::Exit(1);
}
++i;
} else if (arg[0] == '-') {
@@ -1014,12 +1022,10 @@
Handle<String> source = ReadFile(arg);
if (source.IsEmpty()) {
printf("Error reading '%s'\n", arg);
- ExitShell(1);
- return;
+ Shell::Exit(1);
}
if (!Shell::ExecuteString(source, file_name, false, true)) {
- ExitShell(1);
- return;
+ Shell::Exit(1);
}
}
}
@@ -1155,14 +1161,18 @@
return false;
#endif // V8_SHARED
options.num_isolates++;
+ } else if (strcmp(argv[i], "-p") == 0) {
+#ifdef V8_SHARED
+ printf("D8 with shared library does not support multi-threading\n");
+ return false;
+#else
+ options.num_parallel_files++;
+#endif // V8_SHARED
}
#ifdef V8_SHARED
else if (strcmp(argv[i], "--dump-counters") == 0) {
printf("D8 with shared library does not include counters\n");
return false;
- } else if (strcmp(argv[i], "-p") == 0) {
- printf("D8 with shared library does not support multi-threading\n");
- return false;
} else if (strcmp(argv[i], "--debugger") == 0) {
printf("Javascript debugger not included\n");
return false;
@@ -1172,6 +1182,8 @@
#ifndef V8_SHARED
// Run parallel threads if we are not using --isolate
+ options.parallel_files = new char*[options.num_parallel_files];
+ int parallel_files_set = 0;
for (int i = 1; i < argc; i++) {
if (argv[i] == NULL) continue;
if (strcmp(argv[i], "-p") == 0 && i + 1 < argc) {
@@ -1180,25 +1192,21 @@
return false;
}
argv[i] = NULL;
- if (options.parallel_files == NULL) {
- options.parallel_files = new i::List<i::Vector<const char> >();
- }
- int size = 0;
- const char* files = ReadChars(argv[++i], &size);
- if (files == NULL) {
- printf("-p option incomplete\n");
- return false;
- }
+ i++;
+ options.parallel_files[parallel_files_set] = argv[i];
+ parallel_files_set++;
argv[i] = NULL;
- options.parallel_files->Add(i::Vector<const char>(files, size));
- delete[] files;
}
}
+ if (parallel_files_set != options.num_parallel_files) {
+ printf("-p requires a file containing a list of files as parameter\n");
+ return false;
+ }
#endif // V8_SHARED
v8::V8::SetFlagsFromCommandLine(&argc, argv, true);
- // set up isolated source groups
+ // Set up isolated source groups.
options.isolate_sources = new SourceGroup[options.num_isolates];
SourceGroup* current = options.isolate_sources;
current->Begin(argv, 1);
@@ -1221,14 +1229,22 @@
int Shell::RunMain(int argc, char* argv[]) {
#ifndef V8_SHARED
i::List<i::Thread*> threads(1);
- if (options.parallel_files != NULL)
- for (int i = 0; i < options.parallel_files->length(); i++) {
- i::Vector<const char> files = options.parallel_files->at(i);
+ if (options.parallel_files != NULL) {
+ for (int i = 0; i < options.num_parallel_files; i++) {
+ char* files = NULL;
+ { Locker lock(Isolate::GetCurrent());
+ int size = 0;
+ files = ReadChars(options.parallel_files[i], &size);
+ }
+ if (files == NULL) {
+ printf("File list '%s' not found\n", options.parallel_files[i]);
+ Exit(1);
+ }
ShellThread* thread = new ShellThread(threads.length(), files);
thread->Start();
threads.Add(thread);
}
-
+ }
for (int i = 1; i < options.num_isolates; ++i) {
options.isolate_sources[i].StartExecuteInThread();
}
@@ -1250,8 +1266,7 @@
#ifndef V8_SHARED
// Start preemption if threads have been created and preemption is enabled.
- if (options.parallel_files != NULL
- && threads.length() > 0
+ if (threads.length() > 0
&& options.use_preemption) {
Locker::StartPreemption(options.preemption_interval);
}
@@ -1263,12 +1278,16 @@
options.isolate_sources[i].WaitForThread();
}
- if (options.parallel_files != NULL)
- for (int i = 0; i < threads.length(); i++) {
- i::Thread* thread = threads[i];
- thread->Join();
- delete thread;
- }
+ for (int i = 0; i < threads.length(); i++) {
+ i::Thread* thread = threads[i];
+ thread->Join();
+ delete thread;
+ }
+
+ if (threads.length() > 0 && options.use_preemption) {
+ Locker lock;
+ Locker::StopPreemption();
+ }
#endif // V8_SHARED
return 0;
}
diff --git a/src/d8.h b/src/d8.h
index 052b7e0..15d8d5d 100644
--- a/src/d8.h
+++ b/src/d8.h
@@ -1,4 +1,4 @@
-// Copyright 2009 the V8 project authors. All rights reserved.
+// Copyright 2011 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:
@@ -116,6 +116,29 @@
#endif // V8_SHARED
+#ifndef V8_SHARED
+class LineEditor {
+ public:
+ enum Type { DUMB = 0, READLINE = 1 };
+ LineEditor(Type type, const char* name);
+ virtual ~LineEditor() { }
+
+ virtual i::SmartArrayPointer<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_;
+};
+#endif // V8_SHARED
+
+
class SourceGroup {
public:
SourceGroup() :
@@ -180,6 +203,7 @@
#ifndef V8_SHARED
use_preemption(true),
preemption_interval(10),
+ num_parallel_files(0),
parallel_files(NULL),
#endif // V8_SHARED
script_executed(false),
@@ -191,10 +215,18 @@
num_isolates(1),
isolate_sources(NULL) { }
+ ~ShellOptions() {
+#ifndef V8_SHARED
+ delete[] parallel_files;
+#endif // V8_SHARED
+ delete[] isolate_sources;
+ }
+
#ifndef V8_SHARED
bool use_preemption;
int preemption_interval;
- i::List< i::Vector<const char> >* parallel_files;
+ int num_parallel_files;
+ char** parallel_files;
#endif // V8_SHARED
bool script_executed;
bool last_run;
@@ -223,6 +255,7 @@
static Persistent<Context> CreateEvaluationContext();
static int RunMain(int argc, char* argv[]);
static int Main(int argc, char* argv[]);
+ static void Exit(int exit_code);
#ifndef V8_SHARED
static Handle<Array> GetCompletions(Handle<String> text,
@@ -303,6 +336,8 @@
static void AddOSMethods(Handle<ObjectTemplate> os_template);
#ifndef V8_SHARED
static const char* kHistoryFileName;
+ static const int kMaxHistoryEntries;
+ static LineEditor* console;
#endif // V8_SHARED
static const char* kPrompt;
static ShellOptions options;
@@ -333,29 +368,6 @@
};
-#ifndef V8_SHARED
-class LineEditor {
- public:
- enum Type { DUMB = 0, READLINE = 1 };
- LineEditor(Type type, const char* name);
- virtual ~LineEditor() { }
-
- virtual i::SmartArrayPointer<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_;
-};
-#endif // V8_SHARED
-
-
} // namespace v8
diff --git a/src/debug.cc b/src/debug.cc
index 2d58ce1..a229d39 100644
--- a/src/debug.cc
+++ b/src/debug.cc
@@ -40,6 +40,7 @@
#include "global-handles.h"
#include "ic.h"
#include "ic-inl.h"
+#include "list.h"
#include "messages.h"
#include "natives.h"
#include "stub-cache.h"
@@ -542,6 +543,7 @@
thread_local_.last_statement_position_ = RelocInfo::kNoPosition;
thread_local_.step_count_ = 0;
thread_local_.last_fp_ = 0;
+ thread_local_.queued_step_count_ = 0;
thread_local_.step_into_fp_ = 0;
thread_local_.step_out_fp_ = 0;
thread_local_.after_break_target_ = 0;
@@ -957,14 +959,49 @@
// Clear all current stepping setup.
ClearStepping();
- // Notify the debug event listeners.
- isolate_->debugger()->OnDebugBreak(break_points_hit, false);
+ if (thread_local_.queued_step_count_ > 0) {
+ // Perform queued steps
+ int step_count = thread_local_.queued_step_count_;
+
+ // Clear queue
+ thread_local_.queued_step_count_ = 0;
+
+ PrepareStep(StepNext, step_count);
+ } else {
+ // Notify the debug event listeners.
+ isolate_->debugger()->OnDebugBreak(break_points_hit, false);
+ }
} else if (thread_local_.last_step_action_ != StepNone) {
// Hold on to last step action as it is cleared by the call to
// ClearStepping.
StepAction step_action = thread_local_.last_step_action_;
int step_count = thread_local_.step_count_;
+ // If StepNext goes deeper in code, StepOut until original frame
+ // and keep step count queued up in the meantime.
+ if (step_action == StepNext && frame->fp() < thread_local_.last_fp_) {
+ // Count frames until target frame
+ int count = 0;
+ JavaScriptFrameIterator it(isolate_);
+ while (!it.done() && it.frame()->fp() != thread_local_.last_fp_) {
+ count++;
+ it.Advance();
+ }
+
+ // If we found original frame
+ if (it.frame()->fp() == thread_local_.last_fp_) {
+ if (step_count > 1) {
+ // Save old count and action to continue stepping after
+ // StepOut
+ thread_local_.queued_step_count_ = step_count - 1;
+ }
+
+ // Set up for StepOut to reach target frame
+ step_action = StepOut;
+ step_count = count;
+ }
+ }
+
// Clear all current stepping setup.
ClearStepping();
@@ -1105,6 +1142,8 @@
int* source_position) {
HandleScope scope(isolate_);
+ PrepareForBreakPoints();
+
if (!EnsureDebugInfo(shared)) {
// Return if retrieving debug info failed.
return;
@@ -1178,6 +1217,7 @@
void Debug::FloodWithOneShot(Handle<SharedFunctionInfo> shared) {
+ PrepareForBreakPoints();
// Make sure the function has setup the debug info.
if (!EnsureDebugInfo(shared)) {
// Return if we failed to retrieve the debug info.
@@ -1234,6 +1274,9 @@
void Debug::PrepareStep(StepAction step_action, int step_count) {
HandleScope scope(isolate_);
+
+ PrepareForBreakPoints();
+
ASSERT(Debug::InDebugger());
// Remember this step action and count.
@@ -1448,6 +1491,13 @@
// steps before reporting break back to the debugger.
bool Debug::StepNextContinue(BreakLocationIterator* break_location_iterator,
JavaScriptFrame* frame) {
+ // StepNext and StepOut shouldn't bring us deeper in code, so last frame
+ // shouldn't be a parent of current frame.
+ if (thread_local_.last_step_action_ == StepNext ||
+ thread_local_.last_step_action_ == StepOut) {
+ if (frame->fp() < thread_local_.last_fp_) return true;
+ }
+
// If the step last action was step next or step in make sure that a new
// statement is hit.
if (thread_local_.last_step_action_ == StepNext ||
@@ -1676,19 +1726,63 @@
}
-// Ensures the debug information is present for shared.
-bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) {
- // Return if we already have the debug info for shared.
- if (HasDebugInfo(shared)) return true;
-
- // Ensure shared in compiled. Return false if this failed.
- if (!EnsureCompiled(shared, CLEAR_EXCEPTION)) return false;
-
+void Debug::PrepareForBreakPoints() {
// If preparing for the first break point make sure to deoptimize all
// functions as debugging does not work with optimized code.
if (!has_break_points_) {
Deoptimizer::DeoptimizeAll();
+
+ AssertNoAllocation no_allocation;
+ Builtins* builtins = isolate_->builtins();
+ Code* lazy_compile = builtins->builtin(Builtins::kLazyCompile);
+
+ // Find all non-optimized code functions with activation frames on
+ // the stack.
+ List<JSFunction*> active_functions(100);
+ for (JavaScriptFrameIterator it(isolate_); !it.done(); it.Advance()) {
+ JavaScriptFrame* frame = it.frame();
+ if (frame->function()->IsJSFunction()) {
+ JSFunction* function = JSFunction::cast(frame->function());
+ if (function->code()->kind() == Code::FUNCTION)
+ active_functions.Add(function);
+ }
+ }
+ active_functions.Sort();
+
+ // Scan the heap for all non-optimized functions which has no
+ // debug break slots.
+ HeapIterator iterator;
+ HeapObject* obj = NULL;
+ while (((obj = iterator.next()) != NULL)) {
+ if (obj->IsJSFunction()) {
+ JSFunction* function = JSFunction::cast(obj);
+ if (function->shared()->allows_lazy_compilation() &&
+ function->shared()->script()->IsScript() &&
+ function->code()->kind() == Code::FUNCTION &&
+ !function->code()->has_debug_break_slots()) {
+ bool has_activation =
+ SortedListBSearch<JSFunction*>(active_functions, function) != -1;
+ if (!has_activation) {
+ function->set_code(lazy_compile);
+ function->shared()->set_code(lazy_compile);
+ }
+ }
+ }
+ }
}
+}
+
+
+// Ensures the debug information is present for shared.
+bool Debug::EnsureDebugInfo(Handle<SharedFunctionInfo> shared) {
+ // Return if we already have the debug info for shared.
+ if (HasDebugInfo(shared)) {
+ ASSERT(shared->is_compiled());
+ return true;
+ }
+
+ // Ensure shared in compiled. Return false if this failed.
+ if (!EnsureCompiled(shared, CLEAR_EXCEPTION)) return false;
// Create the debug info object.
Handle<DebugInfo> debug_info = FACTORY->NewDebugInfo(shared);
@@ -1739,6 +1833,8 @@
void Debug::SetAfterBreakTarget(JavaScriptFrame* frame) {
HandleScope scope(isolate_);
+ PrepareForBreakPoints();
+
// Get the executing function in which the debug break occurred.
Handle<SharedFunctionInfo> shared =
Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
@@ -1829,6 +1925,8 @@
return false;
}
+ PrepareForBreakPoints();
+
// Get the executing function in which the debug break occurred.
Handle<SharedFunctionInfo> shared =
Handle<SharedFunctionInfo>(JSFunction::cast(frame->function())->shared());
diff --git a/src/debug.h b/src/debug.h
index d39ea4e..a098040 100644
--- a/src/debug.h
+++ b/src/debug.h
@@ -247,6 +247,8 @@
static Handle<DebugInfo> GetDebugInfo(Handle<SharedFunctionInfo> shared);
static bool HasDebugInfo(Handle<SharedFunctionInfo> shared);
+ void PrepareForBreakPoints();
+
// Returns whether the operation succeeded.
bool EnsureDebugInfo(Handle<SharedFunctionInfo> shared);
@@ -506,6 +508,9 @@
// Frame pointer from last step next action.
Address last_fp_;
+ // Number of queued steps left to perform before debug event.
+ int queued_step_count_;
+
// Frame pointer for frame from which step in was performed.
Address step_into_fp_;
diff --git a/src/disassembler.cc b/src/disassembler.cc
index cce3c45..1e67b4c 100644
--- a/src/disassembler.cc
+++ b/src/disassembler.cc
@@ -247,9 +247,6 @@
PropertyType type = code->type();
out.AddFormatted(", %s", Code::PropertyType2String(type));
}
- if (code->ic_in_loop() == IN_LOOP) {
- out.AddFormatted(", in_loop");
- }
if (kind == Code::CALL_IC || kind == Code::KEYED_CALL_IC) {
out.AddFormatted(", argc = %d", code->arguments_count());
}
diff --git a/src/execution.cc b/src/execution.cc
index bdbdca8..f36d4e4 100644
--- a/src/execution.cc
+++ b/src/execution.cc
@@ -149,12 +149,29 @@
Handle<Object> receiver,
int argc,
Object*** args,
- bool* pending_exception) {
+ bool* pending_exception,
+ bool convert_receiver) {
if (!callable->IsJSFunction()) {
callable = TryGetFunctionDelegate(callable, pending_exception);
if (*pending_exception) return callable;
}
Handle<JSFunction> func = Handle<JSFunction>::cast(callable);
+
+ // In non-strict mode, convert receiver.
+ if (convert_receiver && !receiver->IsJSReceiver() &&
+ !func->shared()->native() && !func->shared()->strict_mode()) {
+ if (receiver->IsUndefined() || receiver->IsNull()) {
+ Object* global = func->context()->global()->global_receiver();
+ // Under some circumstances, 'global' can be the JSBuiltinsObject
+ // In that case, don't rewrite.
+ // (FWIW, the same holds for GetIsolate()->global()->global_receiver().)
+ if (!global->IsJSBuiltinsObject()) receiver = Handle<Object>(global);
+ } else {
+ receiver = ToObject(receiver, pending_exception);
+ }
+ if (*pending_exception) return callable;
+ }
+
return Invoke(false, func, receiver, argc, args, pending_exception);
}
@@ -210,10 +227,17 @@
// If you return a function from here, it will be called when an
// attempt is made to call the given object as a function.
+ // If object is a function proxy, get its handler. Iterate if necessary.
+ Object* fun = *object;
+ while (fun->IsJSFunctionProxy()) {
+ fun = JSFunctionProxy::cast(fun)->call_trap();
+ }
+ if (fun->IsJSFunction()) return Handle<Object>(fun);
+
// Objects created through the API can have an instance-call handler
// that should be used when calling the object as a function.
- if (object->IsHeapObject() &&
- HeapObject::cast(*object)->map()->has_instance_call_handler()) {
+ if (fun->IsHeapObject() &&
+ HeapObject::cast(fun)->map()->has_instance_call_handler()) {
return Handle<JSFunction>(
isolate->global_context()->call_as_function_delegate());
}
@@ -227,10 +251,17 @@
ASSERT(!object->IsJSFunction());
Isolate* isolate = Isolate::Current();
+ // If object is a function proxy, get its handler. Iterate if necessary.
+ Object* fun = *object;
+ while (fun->IsJSFunctionProxy()) {
+ fun = JSFunctionProxy::cast(fun)->call_trap();
+ }
+ if (fun->IsJSFunction()) return Handle<Object>(fun);
+
// Objects created through the API can have an instance-call handler
// that should be used when calling the object as a function.
- if (object->IsHeapObject() &&
- HeapObject::cast(*object)->map()->has_instance_call_handler()) {
+ if (fun->IsHeapObject() &&
+ HeapObject::cast(fun)->map()->has_instance_call_handler()) {
return Handle<JSFunction>(
isolate->global_context()->call_as_function_delegate());
}
@@ -253,10 +284,17 @@
// If you return a function from here, it will be called when an
// attempt is made to call the given object as a constructor.
+ // If object is a function proxies, get its handler. Iterate if necessary.
+ Object* fun = *object;
+ while (fun->IsJSFunctionProxy()) {
+ fun = JSFunctionProxy::cast(fun)->call_trap();
+ }
+ if (fun->IsJSFunction()) return Handle<Object>(fun);
+
// Objects created through the API can have an instance-call handler
// that should be used when calling the object as a function.
- if (object->IsHeapObject() &&
- HeapObject::cast(*object)->map()->has_instance_call_handler()) {
+ if (fun->IsHeapObject() &&
+ HeapObject::cast(fun)->map()->has_instance_call_handler()) {
return Handle<JSFunction>(
isolate->global_context()->call_as_constructor_delegate());
}
@@ -274,10 +312,17 @@
// If you return a function from here, it will be called when an
// attempt is made to call the given object as a constructor.
+ // If object is a function proxies, get its handler. Iterate if necessary.
+ Object* fun = *object;
+ while (fun->IsJSFunctionProxy()) {
+ fun = JSFunctionProxy::cast(fun)->call_trap();
+ }
+ if (fun->IsJSFunction()) return Handle<Object>(fun);
+
// Objects created through the API can have an instance-call handler
// that should be used when calling the object as a function.
- if (object->IsHeapObject() &&
- HeapObject::cast(*object)->map()->has_instance_call_handler()) {
+ if (fun->IsHeapObject() &&
+ HeapObject::cast(fun)->map()->has_instance_call_handler()) {
return Handle<JSFunction>(
isolate->global_context()->call_as_constructor_delegate());
}
@@ -553,7 +598,7 @@
Handle<Object> Execution::ToObject(Handle<Object> obj, bool* exc) {
- if (obj->IsJSObject()) return obj;
+ if (obj->IsSpecObject()) return obj;
RETURN_NATIVE_CALL(to_object, 1, { obj.location() }, exc);
}
diff --git a/src/execution.h b/src/execution.h
index bb5f804..5cd7141 100644
--- a/src/execution.h
+++ b/src/execution.h
@@ -53,11 +53,16 @@
// *pending_exception tells whether the invoke resulted in
// a pending exception.
//
+ // When convert_receiver is set, and the receiver is not an object,
+ // and the function called is not in strict mode, receiver is converted to
+ // an object.
+ //
static Handle<Object> Call(Handle<Object> callable,
Handle<Object> receiver,
int argc,
Object*** args,
- bool* pending_exception);
+ bool* pending_exception,
+ bool convert_receiver = false);
// Construct object from function, the caller supplies an array of
// arguments. Arguments are Object* type. After function returns,
diff --git a/src/factory.cc b/src/factory.cc
index ad6d34b..9728926 100644
--- a/src/factory.cc
+++ b/src/factory.cc
@@ -922,10 +922,19 @@
}
-void Factory::BecomeJSObject(Handle<JSProxy> object) {
+void Factory::BecomeJSObject(Handle<JSReceiver> object) {
CALL_HEAP_FUNCTION_VOID(
isolate(),
- isolate()->heap()->ReinitializeJSProxyAsJSObject(*object));
+ isolate()->heap()->ReinitializeJSReceiver(
+ *object, JS_OBJECT_TYPE, JSObject::kHeaderSize));
+}
+
+
+void Factory::BecomeJSFunction(Handle<JSReceiver> object) {
+ CALL_HEAP_FUNCTION_VOID(
+ isolate(),
+ isolate()->heap()->ReinitializeJSReceiver(
+ *object, JS_FUNCTION_TYPE, JSFunction::kSize));
}
diff --git a/src/factory.h b/src/factory.h
index c748d46..71ae750 100644
--- a/src/factory.h
+++ b/src/factory.h
@@ -260,8 +260,9 @@
Handle<JSProxy> NewJSProxy(Handle<Object> handler, Handle<Object> prototype);
- // Change the type of the argument into a regular JS object and reinitialize.
- void BecomeJSObject(Handle<JSProxy> object);
+ // Change the type of the argument into a JS object/function and reinitialize.
+ void BecomeJSObject(Handle<JSReceiver> object);
+ void BecomeJSFunction(Handle<JSReceiver> object);
Handle<JSFunction> NewFunction(Handle<String> name,
Handle<Object> prototype);
diff --git a/src/full-codegen.cc b/src/full-codegen.cc
index 53ace82..8073874 100644
--- a/src/full-codegen.cc
+++ b/src/full-codegen.cc
@@ -286,11 +286,13 @@
}
unsigned table_offset = cgen.EmitStackCheckTable();
- Code::Flags flags = Code::ComputeFlags(Code::FUNCTION, NOT_IN_LOOP);
+ Code::Flags flags = Code::ComputeFlags(Code::FUNCTION);
Handle<Code> code = CodeGenerator::MakeCodeEpilogue(&masm, flags, info);
code->set_optimizable(info->IsOptimizable());
cgen.PopulateDeoptimizationData(code);
code->set_has_deoptimization_support(info->HasDeoptimizationSupport());
+ code->set_has_debug_break_slots(
+ info->isolate()->debugger()->IsDebuggerActive());
code->set_allow_osr_at_loop_nesting_level(0);
code->set_stack_check_table_offset(table_offset);
CodeGenerator::PrintCode(code, info);
diff --git a/src/handles.cc b/src/handles.cc
index 8c6439b..35c363c 100644
--- a/src/handles.cc
+++ b/src/handles.cc
@@ -921,16 +921,13 @@
}
-static bool CompileLazyFunction(Handle<JSFunction> function,
- ClearExceptionFlag flag,
- InLoopFlag in_loop_flag) {
+bool CompileLazy(Handle<JSFunction> function, ClearExceptionFlag flag) {
bool result = true;
if (function->shared()->is_compiled()) {
function->ReplaceCode(function->shared()->code());
function->shared()->set_code_age(0);
} else {
CompilationInfo info(function);
- if (in_loop_flag == IN_LOOP) info.MarkAsInLoop();
result = CompileLazyHelper(&info, flag);
ASSERT(!result || function->is_compiled());
}
@@ -938,18 +935,6 @@
}
-bool CompileLazy(Handle<JSFunction> function,
- ClearExceptionFlag flag) {
- return CompileLazyFunction(function, flag, NOT_IN_LOOP);
-}
-
-
-bool CompileLazyInLoop(Handle<JSFunction> function,
- ClearExceptionFlag flag) {
- return CompileLazyFunction(function, flag, IN_LOOP);
-}
-
-
bool CompileOptimized(Handle<JSFunction> function,
int osr_ast_id,
ClearExceptionFlag flag) {
diff --git a/src/handles.h b/src/handles.h
index 9bb3b1f..7eaf4de 100644
--- a/src/handles.h
+++ b/src/handles.h
@@ -363,8 +363,6 @@
bool CompileLazy(Handle<JSFunction> function, ClearExceptionFlag flag);
-bool CompileLazyInLoop(Handle<JSFunction> function, ClearExceptionFlag flag);
-
bool CompileOptimized(Handle<JSFunction> function,
int osr_ast_id,
ClearExceptionFlag flag);
diff --git a/src/heap.cc b/src/heap.cc
index 3efcdf1..d018593 100644
--- a/src/heap.cc
+++ b/src/heap.cc
@@ -3415,11 +3415,36 @@
map->set_prototype(prototype);
// Allocate the proxy object.
- Object* result;
+ JSProxy* result;
MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
- if (!maybe_result->ToObject(&result)) return maybe_result;
- JSProxy::cast(result)->set_handler(handler);
- JSProxy::cast(result)->set_padding(Smi::FromInt(0));
+ if (!maybe_result->To<JSProxy>(&result)) return maybe_result;
+ result->InitializeBody(map->instance_size(), Smi::FromInt(0));
+ result->set_handler(handler);
+ return result;
+}
+
+
+MaybeObject* Heap::AllocateJSFunctionProxy(Object* handler,
+ Object* call_trap,
+ Object* construct_trap,
+ Object* prototype) {
+ // Allocate map.
+ // TODO(rossberg): Once we optimize proxies, think about a scheme to share
+ // maps. Will probably depend on the identity of the handler object, too.
+ Map* map;
+ MaybeObject* maybe_map_obj =
+ AllocateMap(JS_FUNCTION_PROXY_TYPE, JSFunctionProxy::kSize);
+ if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
+ map->set_prototype(prototype);
+
+ // Allocate the proxy object.
+ JSFunctionProxy* result;
+ MaybeObject* maybe_result = Allocate(map, NEW_SPACE);
+ if (!maybe_result->To<JSFunctionProxy>(&result)) return maybe_result;
+ result->InitializeBody(map->instance_size(), Smi::FromInt(0));
+ result->set_handler(handler);
+ result->set_call_trap(call_trap);
+ result->set_construct_trap(construct_trap);
return result;
}
@@ -3564,16 +3589,19 @@
}
-MaybeObject* Heap::ReinitializeJSProxyAsJSObject(JSProxy* object) {
+MaybeObject* Heap::ReinitializeJSReceiver(
+ JSReceiver* object, InstanceType type, int size) {
+ ASSERT(type >= FIRST_JS_RECEIVER_TYPE);
+
// Allocate fresh map.
// TODO(rossberg): Once we optimize proxies, cache these maps.
Map* map;
- MaybeObject* maybe_map_obj =
- AllocateMap(JS_OBJECT_TYPE, JSObject::kHeaderSize);
+ MaybeObject* maybe_map_obj = AllocateMap(type, size);
if (!maybe_map_obj->To<Map>(&map)) return maybe_map_obj;
- // Check that the receiver has the same size as a fresh object.
- ASSERT(map->instance_size() == object->map()->instance_size());
+ // Check that the receiver has at least the size of the fresh object.
+ int size_difference = object->map()->instance_size() - map->instance_size();
+ ASSERT(size_difference >= 0);
map->set_prototype(object->map()->prototype());
@@ -3590,6 +3618,28 @@
// Reinitialize the object from the constructor map.
InitializeJSObjectFromMap(JSObject::cast(object),
FixedArray::cast(properties), map);
+
+ // Functions require some minimal initialization.
+ if (type == JS_FUNCTION_TYPE) {
+ String* name;
+ MaybeObject* maybe_name = LookupAsciiSymbol("<freezing call trap>");
+ if (!maybe_name->To<String>(&name)) return maybe_name;
+ SharedFunctionInfo* shared;
+ MaybeObject* maybe_shared = AllocateSharedFunctionInfo(name);
+ if (!maybe_shared->To<SharedFunctionInfo>(&shared)) return maybe_shared;
+ JSFunction* func;
+ MaybeObject* maybe_func =
+ InitializeFunction(JSFunction::cast(object), shared, the_hole_value());
+ if (!maybe_func->To<JSFunction>(&func)) return maybe_func;
+ func->set_context(isolate()->context()->global_context());
+ }
+
+ // Put in filler if the new object is smaller than the old.
+ if (size_difference > 0) {
+ CreateFillerObjectAt(
+ object->address() + map->instance_size(), size_difference);
+ }
+
return object;
}
diff --git a/src/heap.h b/src/heap.h
index cc689df..d81ff6c 100644
--- a/src/heap.h
+++ b/src/heap.h
@@ -440,17 +440,25 @@
// Please note this does not perform a garbage collection.
MUST_USE_RESULT MaybeObject* AllocateFunctionPrototype(JSFunction* function);
- // Allocates a Harmony Proxy.
+ // Allocates a Harmony proxy or function proxy.
// Returns Failure::RetryAfterGC(requested_bytes, space) if the allocation
// failed.
// Please note this does not perform a garbage collection.
MUST_USE_RESULT MaybeObject* AllocateJSProxy(Object* handler,
Object* prototype);
- // Reinitialize a JSProxy into an (empty) JSObject. The receiver
- // must have the same size as an empty object. The object is reinitialized
- // and behaves as an object that has been freshly allocated.
- MUST_USE_RESULT MaybeObject* ReinitializeJSProxyAsJSObject(JSProxy* object);
+ MUST_USE_RESULT MaybeObject* AllocateJSFunctionProxy(Object* handler,
+ Object* call_trap,
+ Object* construct_trap,
+ Object* prototype);
+
+ // Reinitialize a JSReceiver into an (empty) JS object of respective type and
+ // size, but keeping the original prototype. The receiver must have at least
+ // the size of the new object. The object is reinitialized and behaves as an
+ // object that has been freshly allocated.
+ MUST_USE_RESULT MaybeObject* ReinitializeJSReceiver(JSReceiver* object,
+ InstanceType type,
+ int size);
// Reinitialize an JSGlobalProxy based on a constructor. The object
// must have the same size as objects allocated using the
diff --git a/src/hydrogen.cc b/src/hydrogen.cc
index a453651..cca168a 100644
--- a/src/hydrogen.cc
+++ b/src/hydrogen.cc
@@ -649,8 +649,7 @@
PrintF("Crankshaft Compiler - ");
}
CodeGenerator::MakeCodePrologue(info);
- Code::Flags flags =
- Code::ComputeFlags(Code::OPTIMIZED_FUNCTION, NOT_IN_LOOP);
+ Code::Flags flags = Code::ComputeFlags(Code::OPTIMIZED_FUNCTION);
Handle<Code> code =
CodeGenerator::MakeCodeEpilogue(&assembler, flags, info);
generator.FinishCode(code);
@@ -5915,7 +5914,9 @@
CHECK_ALIVE(VisitForValue(call->arguments()->at(0)));
HValue* value = Pop();
HHasInstanceTypeAndBranch* result =
- new(zone()) HHasInstanceTypeAndBranch(value, JS_FUNCTION_TYPE);
+ new(zone()) HHasInstanceTypeAndBranch(value,
+ JS_FUNCTION_TYPE,
+ JS_FUNCTION_PROXY_TYPE);
return ast_context()->ReturnControl(result, call->id());
}
diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc
index 845a073..310ea3d 100644
--- a/src/ia32/builtins-ia32.cc
+++ b/src/ia32/builtins-ia32.cc
@@ -590,16 +590,17 @@
// 2. Get the function to call (passed as receiver) from the stack, check
// if it is a function.
- Label non_function;
+ Label slow, non_function;
// 1 ~ return address.
__ mov(edi, Operand(esp, eax, times_4, 1 * kPointerSize));
__ JumpIfSmi(edi, &non_function);
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
- __ j(not_equal, &non_function);
+ __ j(not_equal, &slow);
// 3a. Patch the first argument if necessary when calling a function.
Label shift_arguments;
+ __ Set(edx, Immediate(0)); // indicate regular JS_FUNCTION
{ Label convert_to_object, use_global_receiver, patch_receiver;
// Change context eagerly in case we need the global receiver.
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
@@ -637,6 +638,7 @@
__ push(ebx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ mov(ebx, eax);
+ __ Set(edx, Immediate(0)); // restore
__ pop(eax);
__ SmiUntag(eax);
@@ -661,14 +663,19 @@
__ jmp(&shift_arguments);
}
- // 3b. Patch the first argument when calling a non-function. The
+ // 3b. Check for function proxy.
+ __ bind(&slow);
+ __ Set(edx, Immediate(1)); // indicate function proxy
+ __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
+ __ j(equal, &shift_arguments);
+ __ bind(&non_function);
+ __ Set(edx, Immediate(2)); // indicate non-function
+
+ // 3c. Patch the first argument when calling a non-function. The
// CALL_NON_FUNCTION builtin expects the non-function callee as
// receiver, so overwrite the first argument which will ultimately
// become the receiver.
- __ bind(&non_function);
__ mov(Operand(esp, eax, times_4, 0), edi);
- // Clear edi to indicate a non-function being called.
- __ Set(edi, Immediate(0));
// 4. Shift arguments and return address one slot down on the stack
// (overwriting the original receiver). Adjust argument count to make
@@ -685,13 +692,26 @@
__ dec(eax); // One fewer argument (first argument is new receiver).
}
- // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
- { Label function;
- __ test(edi, Operand(edi));
- __ j(not_zero, &function);
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin,
+ // or a function proxy via CALL_FUNCTION_PROXY.
+ { Label function, non_proxy;
+ __ test(edx, Operand(edx));
+ __ j(zero, &function);
__ Set(ebx, Immediate(0));
- __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
__ SetCallKind(ecx, CALL_AS_METHOD);
+ __ cmp(Operand(edx), Immediate(1));
+ __ j(not_equal, &non_proxy);
+
+ __ pop(edx); // return address
+ __ push(edi); // re-add proxy object as additional argument
+ __ push(edx);
+ __ inc(eax);
+ __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
+ __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+
+ __ bind(&non_proxy);
+ __ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
__ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET);
__ bind(&function);
@@ -717,13 +737,17 @@
void Builtins::Generate_FunctionApply(MacroAssembler* masm) {
+ static const int kArgumentsOffset = 2 * kPointerSize;
+ static const int kReceiverOffset = 3 * kPointerSize;
+ static const int kFunctionOffset = 4 * kPointerSize;
+
__ EnterInternalFrame();
- __ push(Operand(ebp, 4 * kPointerSize)); // push this
- __ push(Operand(ebp, 2 * kPointerSize)); // push arguments
+ __ push(Operand(ebp, kFunctionOffset)); // push this
+ __ push(Operand(ebp, kArgumentsOffset)); // push arguments
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
- // Check the stack for overflow. We are not trying need to catch
+ // Check the stack for overflow. We are not trying to catch
// interruptions (e.g. debug break and preemption) here, so the "real stack
// limit" is checked.
Label okay;
@@ -756,16 +780,21 @@
__ push(eax); // limit
__ push(Immediate(0)); // index
- // Change context eagerly to get the right global object if
- // necessary.
- __ mov(edi, Operand(ebp, 4 * kPointerSize));
+ // Get the receiver.
+ __ mov(ebx, Operand(ebp, kReceiverOffset));
+
+ // Check that the function is a JS function (otherwise it must be a proxy).
+ Label push_receiver;
+ __ mov(edi, Operand(ebp, kFunctionOffset));
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
+ __ j(not_equal, &push_receiver);
+
+ // Change context eagerly to get the right global object if necessary.
__ mov(esi, FieldOperand(edi, JSFunction::kContextOffset));
// Compute the receiver.
- Label call_to_object, use_global_receiver, push_receiver;
- __ mov(ebx, Operand(ebp, 3 * kPointerSize));
-
// Do not transform the receiver for strict mode functions.
+ Label call_to_object, use_global_receiver;
__ mov(ecx, FieldOperand(edi, JSFunction::kSharedFunctionInfoOffset));
__ test_b(FieldOperand(ecx, SharedFunctionInfo::kStrictModeByteOffset),
1 << SharedFunctionInfo::kStrictModeBitWithinByte);
@@ -814,7 +843,7 @@
__ mov(eax, Operand(ebp, kIndexOffset));
__ jmp(&entry);
__ bind(&loop);
- __ mov(edx, Operand(ebp, 2 * kPointerSize)); // load arguments
+ __ mov(edx, Operand(ebp, kArgumentsOffset)); // load arguments
// Use inline caching to speed up access to arguments.
Handle<Code> ic = masm->isolate()->builtins()->KeyedLoadIC_Initialize();
@@ -837,14 +866,30 @@
__ j(not_equal, &loop);
// Invoke the function.
+ Label call_proxy;
ParameterCount actual(eax);
__ SmiUntag(eax);
- __ mov(edi, Operand(ebp, 4 * kPointerSize));
+ __ mov(edi, Operand(ebp, kFunctionOffset));
+ __ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
+ __ j(not_equal, &call_proxy);
__ InvokeFunction(edi, actual, CALL_FUNCTION,
NullCallWrapper(), CALL_AS_METHOD);
__ LeaveInternalFrame();
__ ret(3 * kPointerSize); // remove this, receiver, and arguments
+
+ // Invoke the function proxy.
+ __ bind(&call_proxy);
+ __ push(edi); // add function proxy as last argument
+ __ inc(eax);
+ __ Set(ebx, Immediate(0));
+ __ SetCallKind(ecx, CALL_AS_METHOD);
+ __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
+ __ call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+
+ __ LeaveInternalFrame();
+ __ ret(3 * kPointerSize); // remove this, receiver, and arguments
}
diff --git a/src/ia32/code-stubs-ia32.cc b/src/ia32/code-stubs-ia32.cc
index 76545ef..1009aaf 100644
--- a/src/ia32/code-stubs-ia32.cc
+++ b/src/ia32/code-stubs-ia32.cc
@@ -4199,7 +4199,7 @@
void CallFunctionStub::Generate(MacroAssembler* masm) {
- Label slow;
+ Label slow, non_function;
// The receiver might implicitly be the global object. This is
// indicated by passing the hole as the receiver to the call
@@ -4224,7 +4224,7 @@
__ mov(edi, Operand(esp, (argc_ + 2) * kPointerSize));
// Check that the function really is a JavaScript function.
- __ JumpIfSmi(edi, &slow);
+ __ JumpIfSmi(edi, &non_function);
// Goto slow case if we do not have a function.
__ CmpObjectType(edi, JS_FUNCTION_TYPE, ecx);
__ j(not_equal, &slow);
@@ -4251,15 +4251,32 @@
// Slow-case: Non-function called.
__ bind(&slow);
+ // Check for function proxy.
+ __ CmpInstanceType(ecx, JS_FUNCTION_PROXY_TYPE);
+ __ j(not_equal, &non_function);
+ __ pop(ecx);
+ __ push(edi); // put proxy as additional argument under return address
+ __ push(ecx);
+ __ Set(eax, Immediate(argc_ + 1));
+ __ Set(ebx, Immediate(0));
+ __ SetCallKind(ecx, CALL_AS_FUNCTION);
+ __ GetBuiltinEntry(edx, Builtins::CALL_FUNCTION_PROXY);
+ {
+ Handle<Code> adaptor =
+ masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
+ __ jmp(adaptor, RelocInfo::CODE_TARGET);
+ }
+
// CALL_NON_FUNCTION expects the non-function callee as receiver (instead
// of the original receiver from the call site).
+ __ bind(&non_function);
__ mov(Operand(esp, (argc_ + 1) * kPointerSize), edi);
__ Set(eax, Immediate(argc_));
__ Set(ebx, Immediate(0));
+ __ SetCallKind(ecx, CALL_AS_METHOD);
__ GetBuiltinEntry(edx, Builtins::CALL_NON_FUNCTION);
Handle<Code> adaptor =
masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
- __ SetCallKind(ecx, CALL_AS_METHOD);
__ jmp(adaptor, RelocInfo::CODE_TARGET);
}
diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc
index 59f7b9c..81c9ccb 100644
--- a/src/ia32/full-codegen-ia32.cc
+++ b/src/ia32/full-codegen-ia32.cc
@@ -2013,9 +2013,8 @@
}
// Record source position of the IC call.
SetSourcePosition(expr->position());
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arg_count, in_loop, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
__ call(ic, mode, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
@@ -2047,9 +2046,8 @@
}
// Record source position of the IC call.
SetSourcePosition(expr->position());
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- Handle<Code> ic = isolate()->stub_cache()->ComputeKeyedCallInitialize(
- arg_count, in_loop);
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count);
__ mov(ecx, Operand(esp, (arg_count + 1) * kPointerSize)); // Key.
__ call(ic, RelocInfo::CODE_TARGET, expr->id());
RecordJSReturnSite(expr);
@@ -2071,8 +2069,7 @@
}
// Record source position for debugger.
SetSourcePosition(expr->position());
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, flags);
+ CallFunctionStub stub(arg_count, flags);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -2166,8 +2163,7 @@
}
// Record source position for debugger.
SetSourcePosition(expr->position());
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_IMPLICIT);
+ CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -3582,10 +3578,9 @@
if (expr->is_jsruntime()) {
// Call the JS runtime function via a call IC.
__ Set(ecx, Immediate(expr->name()));
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
- Handle<Code> ic = isolate()->stub_cache()->ComputeCallInitialize(
- arg_count, in_loop, mode);
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
__ call(ic, mode, expr->id());
// Restore context register.
__ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset));
diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc
index 3c49e60..9b5cc56 100644
--- a/src/ia32/ic-ia32.cc
+++ b/src/ia32/ic-ia32.cc
@@ -832,7 +832,6 @@
// Probe the stub cache.
Code::Flags flags = Code::ComputeFlags(kind,
- NOT_IN_LOOP,
MONOMORPHIC,
extra_ic_state,
NORMAL,
@@ -1237,9 +1236,7 @@
// -----------------------------------
// Probe the stub cache.
- Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC,
- NOT_IN_LOOP,
- MONOMORPHIC);
+ Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC);
Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, eax, ecx, ebx,
edx);
@@ -1339,10 +1336,8 @@
// -- esp[0] : return address
// -----------------------------------
- Code::Flags flags = Code::ComputeFlags(Code::STORE_IC,
- NOT_IN_LOOP,
- MONOMORPHIC,
- strict_mode);
+ Code::Flags flags =
+ Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC, strict_mode);
Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, edx, ecx, ebx,
no_reg);
diff --git a/src/ia32/lithium-codegen-ia32.cc b/src/ia32/lithium-codegen-ia32.cc
index 137d4f4..4e3ea98 100644
--- a/src/ia32/lithium-codegen-ia32.cc
+++ b/src/ia32/lithium-codegen-ia32.cc
@@ -2981,8 +2981,8 @@
ASSERT(ToRegister(instr->result()).is(eax));
int arity = instr->arity();
- Handle<Code> ic = isolate()->stub_cache()->
- ComputeKeyedCallInitialize(arity, NOT_IN_LOOP);
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arity);
CallCode(ic, RelocInfo::CODE_TARGET, instr);
}
@@ -2994,7 +2994,7 @@
int arity = instr->arity();
RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
__ mov(ecx, instr->name());
CallCode(ic, mode, instr);
}
@@ -3005,7 +3005,7 @@
ASSERT(ToRegister(instr->result()).is(eax));
int arity = instr->arity();
- CallFunctionStub stub(arity, NOT_IN_LOOP, RECEIVER_MIGHT_BE_IMPLICIT);
+ CallFunctionStub stub(arity, RECEIVER_MIGHT_BE_IMPLICIT);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
__ Drop(1);
}
@@ -3018,7 +3018,7 @@
int arity = instr->arity();
RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
__ mov(ecx, instr->name());
CallCode(ic, mode, instr);
}
diff --git a/src/ic.cc b/src/ic.cc
index 7c89970..0f76a9a 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -61,8 +61,7 @@
void IC::TraceIC(const char* type,
Handle<Object> name,
State old_state,
- Code* new_target,
- const char* extra_info) {
+ Code* new_target) {
if (FLAG_trace_ic) {
State new_state = StateFrom(new_target,
HEAP->undefined_value(),
@@ -94,10 +93,9 @@
} else {
PrintF("<unknown>");
}
- PrintF(" (%c->%c)%s",
+ PrintF(" (%c->%c)",
TransitionMarkFromState(old_state),
- TransitionMarkFromState(new_state),
- extra_info);
+ TransitionMarkFromState(new_state));
name->Print();
PrintF("]\n");
}
@@ -326,7 +324,6 @@
Code* code =
Isolate::Current()->stub_cache()->FindCallInitialize(
target->arguments_count(),
- target->ic_in_loop(),
contextual ? RelocInfo::CODE_TARGET_CONTEXT : RelocInfo::CODE_TARGET,
target->kind());
SetTargetAtAddress(address, code);
@@ -604,13 +601,11 @@
Handle<Object> object,
Handle<String> name) {
int argc = target()->arguments_count();
- InLoopFlag in_loop = target()->ic_in_loop();
MaybeObject* maybe_code = NULL;
switch (lookup->type()) {
case FIELD: {
int index = lookup->GetFieldIndex();
maybe_code = isolate()->stub_cache()->ComputeCallField(argc,
- in_loop,
kind_,
extra_ic_state,
*name,
@@ -626,7 +621,6 @@
JSFunction* function = lookup->GetConstantFunction();
maybe_code =
isolate()->stub_cache()->ComputeCallConstant(argc,
- in_loop,
kind_,
extra_ic_state,
*name,
@@ -646,7 +640,6 @@
if (!cell->value()->IsJSFunction()) return NULL;
JSFunction* function = JSFunction::cast(cell->value());
maybe_code = isolate()->stub_cache()->ComputeCallGlobal(argc,
- in_loop,
kind_,
extra_ic_state,
*name,
@@ -661,7 +654,6 @@
// applicable.
if (lookup->holder() != *receiver) return NULL;
maybe_code = isolate()->stub_cache()->ComputeCallNormal(argc,
- in_loop,
kind_,
extra_ic_state,
*name,
@@ -706,7 +698,6 @@
// Compute the number of arguments.
int argc = target()->arguments_count();
- InLoopFlag in_loop = target()->ic_in_loop();
MaybeObject* maybe_code = NULL;
bool had_proto_failure = false;
if (state == UNINITIALIZED) {
@@ -715,7 +706,6 @@
// setting the monomorphic state.
maybe_code =
isolate()->stub_cache()->ComputeCallPreMonomorphic(argc,
- in_loop,
kind_,
extra_ic_state);
} else if (state == MONOMORPHIC) {
@@ -739,7 +729,6 @@
} else {
maybe_code =
isolate()->stub_cache()->ComputeCallMegamorphic(argc,
- in_loop,
kind_,
extra_ic_state);
}
@@ -776,7 +765,7 @@
#ifdef DEBUG
if (had_proto_failure) state = MONOMORPHIC_PROTOTYPE_FAILURE;
TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC",
- name, state, target(), in_loop ? " (in-loop)" : "");
+ name, state, target());
#endif
}
@@ -797,31 +786,28 @@
if (FLAG_use_ic && state != MEGAMORPHIC && object->IsHeapObject()) {
int argc = target()->arguments_count();
- InLoopFlag in_loop = target()->ic_in_loop();
Heap* heap = Handle<HeapObject>::cast(object)->GetHeap();
Map* map = heap->non_strict_arguments_elements_map();
if (object->IsJSObject() &&
Handle<JSObject>::cast(object)->elements()->map() == map) {
MaybeObject* maybe_code = isolate()->stub_cache()->ComputeCallArguments(
- argc, in_loop, Code::KEYED_CALL_IC);
+ argc, Code::KEYED_CALL_IC);
Object* code;
if (maybe_code->ToObject(&code)) {
set_target(Code::cast(code));
#ifdef DEBUG
- TraceIC(
- "KeyedCallIC", key, state, target(), in_loop ? " (in-loop)" : "");
+ TraceIC("KeyedCallIC", key, state, target());
#endif
}
} else if (FLAG_use_ic && state != MEGAMORPHIC &&
!object->IsAccessCheckNeeded()) {
MaybeObject* maybe_code = isolate()->stub_cache()->ComputeCallMegamorphic(
- argc, in_loop, Code::KEYED_CALL_IC, Code::kNoExtraICState);
+ argc, Code::KEYED_CALL_IC, Code::kNoExtraICState);
Object* code;
if (maybe_code->ToObject(&code)) {
set_target(Code::cast(code));
#ifdef DEBUG
- TraceIC(
- "KeyedCallIC", key, state, target(), in_loop ? " (in-loop)" : "");
+ TraceIC("KeyedCallIC", key, state, target());
#endif
}
}
@@ -1650,7 +1636,6 @@
PolymorphicCodeCache* cache = isolate()->heap()->polymorphic_code_cache();
Code::Flags flags = Code::ComputeFlags(this->kind(),
- NOT_IN_LOOP,
MEGAMORPHIC,
strict_mode);
Object* maybe_cached_stub = cache->Lookup(&target_receiver_maps, flags);
@@ -1905,16 +1890,11 @@
//
static JSFunction* CompileFunction(Isolate* isolate,
- JSFunction* function,
- InLoopFlag in_loop) {
+ JSFunction* function) {
// Compile now with optimization.
HandleScope scope(isolate);
Handle<JSFunction> function_handle(function, isolate);
- if (in_loop == IN_LOOP) {
- CompileLazyInLoop(function_handle, CLEAR_EXCEPTION);
- } else {
- CompileLazy(function_handle, CLEAR_EXCEPTION);
- }
+ CompileLazy(function_handle, CLEAR_EXCEPTION);
return *function_handle;
}
@@ -1943,9 +1923,7 @@
if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
return result;
}
- return CompileFunction(isolate,
- JSFunction::cast(result),
- ic.target()->ic_in_loop());
+ return CompileFunction(isolate, JSFunction::cast(result));
}
@@ -1964,9 +1942,7 @@
if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
return result;
}
- return CompileFunction(isolate,
- JSFunction::cast(result),
- ic.target()->ic_in_loop());
+ return CompileFunction(isolate, JSFunction::cast(result));
}
diff --git a/src/ic.h b/src/ic.h
index 16819e5..ece5be9 100644
--- a/src/ic.h
+++ b/src/ic.h
@@ -149,8 +149,7 @@
void TraceIC(const char* type,
Handle<Object> name,
State old_state,
- Code* new_target,
- const char* extra_info = "");
+ Code* new_target);
#endif
Failure* TypeError(const char* type,
diff --git a/src/json.js b/src/json.js
index a491bcc..deba126 100644
--- a/src/json.js
+++ b/src/json.js
@@ -54,7 +54,7 @@
function JSONParse(text, reviver) {
var unfiltered = %ParseJson(TO_STRING_INLINE(text));
- if (IS_FUNCTION(reviver)) {
+ if (IS_SPEC_FUNCTION(reviver)) {
return Revive({'': unfiltered}, '', reviver);
} else {
return unfiltered;
@@ -143,11 +143,11 @@
var value = holder[key];
if (IS_SPEC_OBJECT(value)) {
var toJSON = value.toJSON;
- if (IS_FUNCTION(toJSON)) {
+ if (IS_SPEC_FUNCTION(toJSON)) {
value = %_CallFunction(value, key, toJSON);
}
}
- if (IS_FUNCTION(replacer)) {
+ if (IS_SPEC_FUNCTION(replacer)) {
value = %_CallFunction(holder, key, value, replacer);
}
if (IS_STRING(value)) {
@@ -273,7 +273,7 @@
function BasicJSONSerialize(key, value, stack, builder) {
if (IS_SPEC_OBJECT(value)) {
var toJSON = value.toJSON;
- if (IS_FUNCTION(toJSON)) {
+ if (IS_SPEC_FUNCTION(toJSON)) {
value = %_CallFunction(value, ToString(key), toJSON);
}
}
diff --git a/src/list-inl.h b/src/list-inl.h
index 8ef7514..80bccc9 100644
--- a/src/list-inl.h
+++ b/src/list-inl.h
@@ -207,6 +207,35 @@
}
+template <typename T>
+int SortedListBSearch(
+ const List<T>& list, T elem, int (*cmp)(const T* x, const T* y)) {
+ int low = 0;
+ int high = list.length() - 1;
+ while (low <= high) {
+ int mid = (low + high) / 2;
+ T mid_elem = list[mid];
+
+ if (mid_elem > elem) {
+ high = mid - 1;
+ continue;
+ }
+ if (mid_elem < elem) {
+ low = mid + 1;
+ continue;
+ }
+ // Found the elememt.
+ return mid;
+ }
+ return -1;
+}
+
+
+template <typename T>
+int SortedListBSearch(const List<T>& list, T elem) {
+ return SortedListBSearch<T>(list, elem, PointerValueCompare<T>);
+}
+
} } // namespace v8::internal
#endif // V8_LIST_INL_H_
diff --git a/src/list.h b/src/list.h
index 33267bd..0558709 100644
--- a/src/list.h
+++ b/src/list.h
@@ -168,6 +168,15 @@
typedef List<Map*> MapList;
typedef List<Code*> CodeList;
+// Perform binary search for an element in an already sorted
+// list. Returns the index of the element of -1 if it was not found.
+template <typename T>
+int SortedListBSearch(
+ const List<T>& list, T elem, int (*cmp)(const T* x, const T* y));
+template <typename T>
+int SortedListBSearch(const List<T>& list, T elem);
+
} } // namespace v8::internal
+
#endif // V8_LIST_H_
diff --git a/src/log.cc b/src/log.cc
index d21bf3f..3d66b5f 100644
--- a/src/log.cc
+++ b/src/log.cc
@@ -1527,6 +1527,51 @@
}
+void Logger::LogExistingFunction(Handle<SharedFunctionInfo> shared,
+ Handle<Code> code) {
+ Handle<String> func_name(shared->DebugName());
+ if (shared->script()->IsScript()) {
+ Handle<Script> script(Script::cast(shared->script()));
+ if (script->name()->IsString()) {
+ Handle<String> script_name(String::cast(script->name()));
+ int line_num = GetScriptLineNumber(script, shared->start_position());
+ if (line_num > 0) {
+ PROFILE(ISOLATE,
+ CodeCreateEvent(
+ Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
+ *code, *shared,
+ *script_name, line_num + 1));
+ } else {
+ // Can't distinguish eval and script here, so always use Script.
+ PROFILE(ISOLATE,
+ CodeCreateEvent(
+ Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
+ *code, *shared, *script_name));
+ }
+ } else {
+ PROFILE(ISOLATE,
+ CodeCreateEvent(
+ Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
+ *code, *shared, *func_name));
+ }
+ } else if (shared->IsApiFunction()) {
+ // API function.
+ FunctionTemplateInfo* fun_data = shared->get_api_func_data();
+ Object* raw_call_data = fun_data->call_code();
+ if (!raw_call_data->IsUndefined()) {
+ CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
+ Object* callback_obj = call_data->callback();
+ Address entry_point = v8::ToCData<Address>(callback_obj);
+ PROFILE(ISOLATE, CallbackEvent(*func_name, entry_point));
+ }
+ } else {
+ PROFILE(ISOLATE,
+ CodeCreateEvent(
+ Logger::LAZY_COMPILE_TAG, *code, *shared, *func_name));
+ }
+}
+
+
void Logger::LogCompiledFunctions() {
HandleScope scope;
const int compiled_funcs_count = EnumerateCompiledFunctions(NULL, NULL);
@@ -1540,48 +1585,7 @@
if (*code_objects[i] == Isolate::Current()->builtins()->builtin(
Builtins::kLazyCompile))
continue;
- Handle<SharedFunctionInfo> shared = sfis[i];
- Handle<String> func_name(shared->DebugName());
- if (shared->script()->IsScript()) {
- Handle<Script> script(Script::cast(shared->script()));
- if (script->name()->IsString()) {
- Handle<String> script_name(String::cast(script->name()));
- int line_num = GetScriptLineNumber(script, shared->start_position());
- if (line_num > 0) {
- PROFILE(ISOLATE,
- CodeCreateEvent(
- Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
- *code_objects[i], *shared,
- *script_name, line_num + 1));
- } else {
- // Can't distinguish eval and script here, so always use Script.
- PROFILE(ISOLATE,
- CodeCreateEvent(
- Logger::ToNativeByScript(Logger::SCRIPT_TAG, *script),
- *code_objects[i], *shared, *script_name));
- }
- } else {
- PROFILE(ISOLATE,
- CodeCreateEvent(
- Logger::ToNativeByScript(Logger::LAZY_COMPILE_TAG, *script),
- *code_objects[i], *shared, *func_name));
- }
- } else if (shared->IsApiFunction()) {
- // API function.
- FunctionTemplateInfo* fun_data = shared->get_api_func_data();
- Object* raw_call_data = fun_data->call_code();
- if (!raw_call_data->IsUndefined()) {
- CallHandlerInfo* call_data = CallHandlerInfo::cast(raw_call_data);
- Object* callback_obj = call_data->callback();
- Address entry_point = v8::ToCData<Address>(callback_obj);
- PROFILE(ISOLATE, CallbackEvent(*func_name, entry_point));
- }
- } else {
- PROFILE(ISOLATE,
- CodeCreateEvent(
- Logger::LAZY_COMPILE_TAG, *code_objects[i],
- *shared, *func_name));
- }
+ LogExistingFunction(sfis[i], code_objects[i]);
}
}
diff --git a/src/log.h b/src/log.h
index 0225059..fe19810 100644
--- a/src/log.h
+++ b/src/log.h
@@ -281,6 +281,8 @@
void ResumeProfiler();
bool IsProfilerPaused();
+ void LogExistingFunction(Handle<SharedFunctionInfo> shared,
+ Handle<Code> code);
// Logs all compiled functions found in the heap.
void LogCompiledFunctions();
// Logs all accessor callbacks found in the heap.
diff --git a/src/macros.py b/src/macros.py
index e3d1e03..7a493ca 100644
--- a/src/macros.py
+++ b/src/macros.py
@@ -116,9 +116,17 @@
# Macro for ECMAScript 5 queries of the type:
# "Type(O) is object."
-# This is the same as being either a function or an object in V8 terminology.
+# This is the same as being either a function or an object in V8 terminology
+# (including proxies).
# In addition, an undetectable object is also included by this.
-macro IS_SPEC_OBJECT(arg) = (%_IsSpecObject(arg));
+macro IS_SPEC_OBJECT(arg) = (%_IsSpecObject(arg));
+
+# Macro for ECMAScript 5 queries of the type:
+# "IsCallable(O)"
+# We assume here that this is the same as being either a function or a function
+# proxy. That ignores host objects with [[Call]] methods, but in most situations
+# we cannot handle those anyway.
+macro IS_SPEC_FUNCTION(arg) = (%_ClassOf(arg) === 'Function');
# Inline macros. Use %IS_VAR to make sure arg is evaluated only once.
macro NUMBER_IS_NAN(arg) = (!%_IsSmi(%IS_VAR(arg)) && !(arg == arg));
diff --git a/src/messages.js b/src/messages.js
index 4adcb30..a9993af 100644
--- a/src/messages.js
+++ b/src/messages.js
@@ -185,6 +185,7 @@
"define_disallowed", ["Cannot define property:", "%0", ", object is not extensible."],
"non_extensible_proto", ["%0", " is not extensible"],
"handler_non_object", ["Proxy.", "%0", " called with non-object as handler"],
+ "trap_function_expected", ["Proxy.", "%0", " called with non-function for ", "%1", " trap"],
"handler_trap_missing", ["Proxy handler ", "%0", " has no '", "%1", "' trap"],
"handler_trap_must_be_callable", ["Proxy handler ", "%0", " has non-callable '", "%1", "' trap"],
"handler_returned_false", ["Proxy handler ", "%0", " returned false for '", "%1", "' trap"],
diff --git a/src/mips/assembler-mips-inl.h b/src/mips/assembler-mips-inl.h
index b5ffe73..c4c4fd2 100644
--- a/src/mips/assembler-mips-inl.h
+++ b/src/mips/assembler-mips-inl.h
@@ -83,6 +83,14 @@
// RelocInfo.
void RelocInfo::apply(intptr_t delta) {
+ if (IsCodeTarget(rmode_)) {
+ uint32_t scope1 = (uint32_t) target_address() & ~kImm28Mask;
+ uint32_t scope2 = reinterpret_cast<uint32_t>(pc_) & ~kImm28Mask;
+
+ if (scope1 != scope2) {
+ Assembler::JumpLabelToJumpRegister(pc_);
+ }
+ }
if (IsInternalReference(rmode_)) {
// Absolute code pointer inside code object moves with the code object.
byte* p = reinterpret_cast<byte*>(pc_);
@@ -218,8 +226,9 @@
Instr instr2 = Assembler::instr_at(pc_ + 2 * Assembler::kInstrSize);
bool patched_return = ((instr0 & kOpcodeMask) == LUI &&
(instr1 & kOpcodeMask) == ORI &&
- (instr2 & kOpcodeMask) == SPECIAL &&
- (instr2 & kFunctionFieldMask) == JALR);
+ ((instr2 & kOpcodeMask) == JAL ||
+ ((instr2 & kOpcodeMask) == SPECIAL &&
+ (instr2 & kFunctionFieldMask) == JALR)));
return patched_return;
}
diff --git a/src/mips/assembler-mips.cc b/src/mips/assembler-mips.cc
index f30f38b..e01a0ca 100644
--- a/src/mips/assembler-mips.cc
+++ b/src/mips/assembler-mips.cc
@@ -172,7 +172,8 @@
// -----------------------------------------------------------------------------
// Implementation of RelocInfo.
-const int RelocInfo::kApplyMask = 1 << RelocInfo::INTERNAL_REFERENCE;
+const int RelocInfo::kApplyMask = RelocInfo::kCodeTargetMask |
+ 1 << RelocInfo::INTERNAL_REFERENCE;
bool RelocInfo::IsCodedSpecially() {
@@ -546,6 +547,19 @@
}
+bool Assembler::IsJal(Instr instr) {
+ return GetOpcodeField(instr) == JAL;
+}
+
+bool Assembler::IsJr(Instr instr) {
+ return GetOpcodeField(instr) == SPECIAL && GetFunctionField(instr) == JR;
+}
+
+bool Assembler::IsJalr(Instr instr) {
+ return GetOpcodeField(instr) == SPECIAL && GetFunctionField(instr) == JALR;
+}
+
+
bool Assembler::IsLui(Instr instr) {
uint32_t opcode = GetOpcodeField(instr);
// Checks if the instruction is a load upper immediate.
@@ -939,7 +953,7 @@
void Assembler::GenInstrJump(Opcode opcode,
- uint32_t address) {
+ uint32_t address) {
BlockTrampolinePoolScope block_trampoline_pool(this);
ASSERT(is_uint26(address));
Instr instr = opcode | address;
@@ -1112,7 +1126,12 @@
void Assembler::j(int32_t target) {
- ASSERT(is_uint28(target) && ((target & 3) == 0));
+#if DEBUG
+ // Get pc of delay slot.
+ uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
+ bool in_range = ((uint32_t)(ipc^target) >> (kImm26Bits+kImmFieldShift)) == 0;
+ ASSERT(in_range && ((target & 3) == 0));
+#endif
GenInstrJump(J, target >> 2);
}
@@ -1128,8 +1147,13 @@
void Assembler::jal(int32_t target) {
+#ifdef DEBUG
+ // Get pc of delay slot.
+ uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
+ bool in_range = ((uint32_t)(ipc^target) >> (kImm26Bits+kImmFieldShift)) == 0;
+ ASSERT(in_range && ((target & 3) == 0));
+#endif
positions_recorder()->WriteRecordedPositions();
- ASSERT(is_uint28(target) && ((target & 3) == 0));
GenInstrJump(JAL, target >> 2);
}
@@ -1142,6 +1166,32 @@
}
+void Assembler::j_or_jr(int32_t target, Register rs) {
+ // Get pc of delay slot.
+ uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
+ bool in_range = ((uint32_t)(ipc^target) >> (kImm26Bits+kImmFieldShift)) == 0;
+
+ if (in_range) {
+ j(target);
+ } else {
+ jr(t9);
+ }
+}
+
+
+void Assembler::jal_or_jalr(int32_t target, Register rs) {
+ // Get pc of delay slot.
+ uint32_t ipc = reinterpret_cast<uint32_t>(pc_ + 1 * kInstrSize);
+ bool in_range = ((uint32_t)(ipc^target) >> (kImm26Bits+kImmFieldShift)) == 0;
+
+ if (in_range) {
+ jal(target);
+ } else {
+ jalr(t9);
+ }
+}
+
+
//-------Data-processing-instructions---------
// Arithmetic.
@@ -1614,6 +1664,13 @@
GenInstrRegister(COP1, CFC1, rt, fs);
}
+void Assembler::DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi) {
+ uint64_t i;
+ memcpy(&i, &d, 8);
+
+ *lo = i & 0xffffffff;
+ *hi = i >> 32;
+}
// Arithmetic.
@@ -1972,10 +2029,15 @@
}
if (rinfo.rmode() != RelocInfo::NONE) {
// Don't record external references unless the heap will be serialized.
- if (rmode == RelocInfo::EXTERNAL_REFERENCE &&
- !Serializer::enabled() &&
- !FLAG_debug_code) {
- return;
+ if (rmode == RelocInfo::EXTERNAL_REFERENCE) {
+#ifdef DEBUG
+ if (!Serializer::enabled()) {
+ Serializer::TooLateToEnableNow();
+ }
+#endif
+ if (!Serializer::enabled() && !emit_debug_code()) {
+ return;
+ }
}
ASSERT(buffer_space() >= kMaxRelocSize); // Too late to grow buffer here.
if (rmode == RelocInfo::CODE_TARGET_WITH_ID) {
@@ -2070,30 +2132,142 @@
}
+// On Mips, a target address is stored in a lui/ori instruction pair, each
+// of which load 16 bits of the 32-bit address to a register.
+// Patching the address must replace both instr, and flush the i-cache.
+//
+// There is an optimization below, which emits a nop when the address
+// fits in just 16 bits. This is unlikely to help, and should be benchmarked,
+// and possibly removed.
void Assembler::set_target_address_at(Address pc, Address target) {
- // On MIPS we patch the address into lui/ori instruction pair.
-
- // First check we have an li (lui/ori pair).
Instr instr2 = instr_at(pc + kInstrSize);
-#ifdef DEBUG
- Instr instr1 = instr_at(pc);
-
- // Check we have indeed the result from a li with MustUseReg true.
- CHECK((GetOpcodeField(instr1) == LUI && GetOpcodeField(instr2) == ORI));
-#endif
-
uint32_t rt_code = GetRtField(instr2);
uint32_t* p = reinterpret_cast<uint32_t*>(pc);
uint32_t itarget = reinterpret_cast<uint32_t>(target);
- // lui rt, high-16.
- // ori rt rt, low-16.
+#ifdef DEBUG
+ // Check we have the result from a li macro-instruction, using instr pair.
+ Instr instr1 = instr_at(pc);
+ CHECK((GetOpcodeField(instr1) == LUI && GetOpcodeField(instr2) == ORI));
+#endif
+
+ // Must use 2 instructions to insure patchable code => just use lui and ori.
+ // lui rt, upper-16.
+ // ori rt rt, lower-16.
*p = LUI | rt_code | ((itarget & kHiMask) >> kLuiShift);
*(p+1) = ORI | rt_code | (rt_code << 5) | (itarget & kImm16Mask);
- CPU::FlushICache(pc, 2 * sizeof(int32_t));
+ // The following code is an optimization for the common case of Call()
+ // or Jump() which is load to register, and jump through register:
+ // li(t9, address); jalr(t9) (or jr(t9)).
+ // If the destination address is in the same 256 MB page as the call, it
+ // is faster to do a direct jal, or j, rather than jump thru register, since
+ // that lets the cpu pipeline prefetch the target address. However each
+ // time the address above is patched, we have to patch the direct jal/j
+ // instruction, as well as possibly revert to jalr/jr if we now cross a
+ // 256 MB page. Note that with the jal/j instructions, we do not need to
+ // load the register, but that code is left, since it makes it easy to
+ // revert this process. A further optimization could try replacing the
+ // li sequence with nops.
+ // This optimization can only be applied if the rt-code from instr2 is the
+ // register used for the jalr/jr. Finally, we have to skip 'jr ra', which is
+ // mips return. Occasionally this lands after an li().
+
+ Instr instr3 = instr_at(pc + 2 * kInstrSize);
+ uint32_t ipc = reinterpret_cast<uint32_t>(pc + 3 * kInstrSize);
+ bool in_range =
+ ((uint32_t)(ipc ^ itarget) >> (kImm26Bits + kImmFieldShift)) == 0;
+ uint32_t target_field = (uint32_t)(itarget & kJumpAddrMask) >> kImmFieldShift;
+ bool patched_jump = false;
+
+#ifndef ALLOW_JAL_IN_BOUNDARY_REGION
+ // This is a workaround to the 24k core E156 bug (affect some 34k cores also).
+ // Since the excluded space is only 64KB out of 256MB (0.02 %), we will just
+ // apply this workaround for all cores so we don't have to identify the core.
+ if (in_range) {
+ // The 24k core E156 bug has some very specific requirements, we only check
+ // the most simple one: if the address of the delay slot instruction is in
+ // the first or last 32 KB of the 256 MB segment.
+ uint32_t segment_mask = ((256 * MB) - 1) ^ ((32 * KB) - 1);
+ uint32_t ipc_segment_addr = ipc & segment_mask;
+ if (ipc_segment_addr == 0 || ipc_segment_addr == segment_mask)
+ in_range = false;
+ }
+#endif
+
+ if (IsJalr(instr3)) {
+ // Try to convert JALR to JAL.
+ if (in_range && GetRt(instr2) == GetRs(instr3)) {
+ *(p+2) = JAL | target_field;
+ patched_jump = true;
+ }
+ } else if (IsJr(instr3)) {
+ // Try to convert JR to J, skip returns (jr ra).
+ bool is_ret = static_cast<int>(GetRs(instr3)) == ra.code();
+ if (in_range && !is_ret && GetRt(instr2) == GetRs(instr3)) {
+ *(p+2) = J | target_field;
+ patched_jump = true;
+ }
+ } else if (IsJal(instr3)) {
+ if (in_range) {
+ // We are patching an already converted JAL.
+ *(p+2) = JAL | target_field;
+ } else {
+ // Patch JAL, but out of range, revert to JALR.
+ // JALR rs reg is the rt reg specified in the ORI instruction.
+ uint32_t rs_field = GetRt(instr2) << kRsShift;
+ uint32_t rd_field = ra.code() << kRdShift; // Return-address (ra) reg.
+ *(p+2) = SPECIAL | rs_field | rd_field | JALR;
+ }
+ patched_jump = true;
+ } else if (IsJ(instr3)) {
+ if (in_range) {
+ // We are patching an already converted J (jump).
+ *(p+2) = J | target_field;
+ } else {
+ // Trying patch J, but out of range, just go back to JR.
+ // JR 'rs' reg is the 'rt' reg specified in the ORI instruction (instr2).
+ uint32_t rs_field = GetRt(instr2) << kRsShift;
+ *(p+2) = SPECIAL | rs_field | JR;
+ }
+ patched_jump = true;
+ }
+
+ CPU::FlushICache(pc, (patched_jump ? 3 : 2) * sizeof(int32_t));
}
+void Assembler::JumpLabelToJumpRegister(Address pc) {
+ // Address pc points to lui/ori instructions.
+ // Jump to label may follow at pc + 2 * kInstrSize.
+ uint32_t* p = reinterpret_cast<uint32_t*>(pc);
+#ifdef DEBUG
+ Instr instr1 = instr_at(pc);
+#endif
+ Instr instr2 = instr_at(pc + 1 * kInstrSize);
+ Instr instr3 = instr_at(pc + 2 * kInstrSize);
+ bool patched = false;
+
+ if (IsJal(instr3)) {
+ ASSERT(GetOpcodeField(instr1) == LUI);
+ ASSERT(GetOpcodeField(instr2) == ORI);
+
+ uint32_t rs_field = GetRt(instr2) << kRsShift;
+ uint32_t rd_field = ra.code() << kRdShift; // Return-address (ra) reg.
+ *(p+2) = SPECIAL | rs_field | rd_field | JALR;
+ patched = true;
+ } else if (IsJ(instr3)) {
+ ASSERT(GetOpcodeField(instr1) == LUI);
+ ASSERT(GetOpcodeField(instr2) == ORI);
+
+ uint32_t rs_field = GetRt(instr2) << kRsShift;
+ *(p+2) = SPECIAL | rs_field | JR;
+ patched = true;
+ }
+
+ if (patched) {
+ CPU::FlushICache(pc+2, sizeof(Address));
+ }
+}
} } // namespace v8::internal
diff --git a/src/mips/assembler-mips.h b/src/mips/assembler-mips.h
index 2c0633f..38e9537 100644
--- a/src/mips/assembler-mips.h
+++ b/src/mips/assembler-mips.h
@@ -168,24 +168,36 @@
// Coprocessor register.
struct FPURegister {
static const int kNumRegisters = v8::internal::kNumFPURegisters;
- // f0 has been excluded from allocation. This is following ia32
- // where xmm0 is excluded.
- static const int kNumAllocatableRegisters = 15;
+
+ // TODO(plind): Warning, inconsistent numbering here. kNumFPURegisters refers
+ // to number of 32-bit FPU regs, but kNumAllocatableRegisters refers to
+ // number of Double regs (64-bit regs, or FPU-reg-pairs).
+
+ // A few double registers are reserved: one as a scratch register and one to
+ // hold 0.0.
+ // f28: 0.0
+ // f30: scratch register.
+ static const int kNumReservedRegisters = 2;
+ static const int kNumAllocatableRegisters = kNumRegisters / 2 -
+ kNumReservedRegisters;
+
static int ToAllocationIndex(FPURegister reg) {
- ASSERT(reg.code() != 0);
ASSERT(reg.code() % 2 == 0);
- return (reg.code() / 2) - 1;
+ ASSERT(reg.code() / 2 < kNumAllocatableRegisters);
+ ASSERT(reg.is_valid());
+ return (reg.code() / 2);
}
static FPURegister FromAllocationIndex(int index) {
ASSERT(index >= 0 && index < kNumAllocatableRegisters);
- return from_code((index + 1) * 2);
+ return from_code(index * 2);
}
static const char* AllocationIndexToString(int index) {
ASSERT(index >= 0 && index < kNumAllocatableRegisters);
const char* const names[] = {
+ "f0",
"f2",
"f4",
"f6",
@@ -198,9 +210,7 @@
"f20",
"f22",
"f24",
- "f26",
- "f28",
- "f30"
+ "f26"
};
return names[index];
}
@@ -212,6 +222,23 @@
bool is_valid() const { return 0 <= code_ && code_ < kNumFPURegisters ; }
bool is(FPURegister creg) const { return code_ == creg.code_; }
+ FPURegister low() const {
+ // Find low reg of a Double-reg pair, which is the reg itself.
+ ASSERT(code_ % 2 == 0); // Specified Double reg must be even.
+ FPURegister reg;
+ reg.code_ = code_;
+ ASSERT(reg.is_valid());
+ return reg;
+ }
+ FPURegister high() const {
+ // Find high reg of a Doubel-reg pair, which is reg + 1.
+ ASSERT(code_ % 2 == 0); // Specified Double reg must be even.
+ FPURegister reg;
+ reg.code_ = code_ + 1;
+ ASSERT(reg.is_valid());
+ return reg;
+ }
+
int code() const {
ASSERT(is_valid());
return code_;
@@ -228,9 +255,19 @@
int code_;
};
-typedef FPURegister DoubleRegister;
+// V8 now supports the O32 ABI, and the FPU Registers are organized as 32
+// 32-bit registers, f0 through f31. When used as 'double' they are used
+// in pairs, starting with the even numbered register. So a double operation
+// on f0 really uses f0 and f1.
+// (Modern mips hardware also supports 32 64-bit registers, via setting
+// (priviledged) Status Register FR bit to 1. This is used by the N32 ABI,
+// but it is not in common use. Someday we will want to support this in v8.)
-const FPURegister no_creg = { -1 };
+// For O32 ABI, Floats and Doubles refer to same set of 32 32-bit registers.
+typedef FPURegister DoubleRegister;
+typedef FPURegister FloatRegister;
+
+const FPURegister no_freg = { -1 };
const FPURegister f0 = { 0 }; // Return value in hard float mode.
const FPURegister f1 = { 1 };
@@ -265,6 +302,8 @@
const FPURegister f30 = { 30 };
const FPURegister f31 = { 31 };
+const FPURegister kDoubleRegZero = f28;
+
// FPU (coprocessor 1) control registers.
// Currently only FCSR (#31) is implemented.
struct FPUControlRegister {
@@ -331,6 +370,10 @@
explicit MemOperand(Register rn, int32_t offset = 0);
int32_t offset() const { return offset_; }
+ bool OffsetIsInt16Encodable() const {
+ return is_int16(offset_);
+ }
+
private:
int32_t offset_;
@@ -504,6 +547,8 @@
static Address target_address_at(Address pc);
static void set_target_address_at(Address pc, Address target);
+ static void JumpLabelToJumpRegister(Address pc);
+
// This sets the branch destination (which gets loaded at the call address).
// This is for calls and branches within generated code.
inline static void set_target_at(Address instruction_payload,
@@ -534,9 +579,13 @@
static const int kExternalTargetSize = 0 * kInstrSize;
// Number of consecutive instructions used to store 32bit constant.
- // Used in RelocInfo::target_address_address() function to tell serializer
- // address of the instruction that follows LUI/ORI instruction pair.
- static const int kInstructionsFor32BitConstant = 2;
+ // Before jump-optimizations, this constant was used in
+ // RelocInfo::target_address_address() function to tell serializer address of
+ // the instruction that follows LUI/ORI instruction pair. Now, with new jump
+ // optimization, where jump-through-register instruction that usually
+ // follows LUI/ORI pair is substituted with J/JAL, this constant equals
+ // to 3 instructions (LUI+ORI+J/JAL/JR/JALR).
+ static const int kInstructionsFor32BitConstant = 3;
// Distance between the instruction referring to the address of the call
// target and the return address.
@@ -623,6 +672,8 @@
void jal(int32_t target);
void jalr(Register rs, Register rd = ra);
void jr(Register target);
+ void j_or_jr(int32_t target, Register rs);
+ void jal_or_jalr(int32_t target, Register rs);
//-------Data-processing-instructions---------
@@ -892,6 +943,10 @@
static bool IsLui(Instr instr);
static bool IsOri(Instr instr);
+ static bool IsJal(Instr instr);
+ static bool IsJr(Instr instr);
+ static bool IsJalr(Instr instr);
+
static bool IsNop(Instr instr, unsigned int type);
static bool IsPop(Instr instr);
static bool IsPush(Instr instr);
@@ -976,6 +1031,8 @@
return internal_trampoline_exception_;
}
+ void DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi);
+
bool is_trampoline_emitted() const {
return trampoline_emitted_;
}
diff --git a/src/mips/constants-mips.cc b/src/mips/constants-mips.cc
index 96a2333..d0a7af5 100644
--- a/src/mips/constants-mips.cc
+++ b/src/mips/constants-mips.cc
@@ -191,6 +191,7 @@
const int op = OpcodeFieldRaw();
switch (op) {
case JAL:
+ return true;
case REGIMM:
switch (RtFieldRaw()) {
case BGEZAL:
@@ -272,7 +273,7 @@
case MOVCI:
return kRegisterType;
default:
- UNREACHABLE();
+ return kUnsupported;
};
break;
case SPECIAL2:
@@ -281,7 +282,7 @@
case CLZ:
return kRegisterType;
default:
- UNREACHABLE();
+ return kUnsupported;
};
break;
case SPECIAL3:
@@ -290,7 +291,7 @@
case EXT:
return kRegisterType;
default:
- UNREACHABLE();
+ return kUnsupported;
};
break;
case COP1: // Coprocessor instructions.
@@ -341,7 +342,7 @@
case JAL:
return kJumpType;
default:
- UNREACHABLE();
+ return kUnsupported;
};
return kUnsupported;
}
diff --git a/src/mips/constants-mips.h b/src/mips/constants-mips.h
index ede9688..d76ae59 100644
--- a/src/mips/constants-mips.h
+++ b/src/mips/constants-mips.h
@@ -204,6 +204,10 @@
static const int kImm28Shift = 0;
static const int kImm28Bits = 28;
+// In branches and jumps immediate fields point to words, not bytes,
+// and are therefore shifted by 2.
+static const int kImmFieldShift = 2;
+
static const int kFsShift = 11;
static const int kFsBits = 5;
static const int kFtShift = 16;
@@ -233,7 +237,7 @@
static const int kHiMask = 0xffff << 16;
static const int kLoMask = 0xffff;
static const int kSignMask = 0x80000000;
-
+static const int kJumpAddrMask = (1 << (kImm26Bits + kImmFieldShift)) - 1;
// ----- MIPS Opcodes and Function Fields.
// We use this presentation to stay close to the table representation in
@@ -290,12 +294,12 @@
enum SecondaryField {
// SPECIAL Encoding of Function Field.
SLL = ((0 << 3) + 0),
+ MOVCI = ((0 << 3) + 1),
SRL = ((0 << 3) + 2),
SRA = ((0 << 3) + 3),
SLLV = ((0 << 3) + 4),
SRLV = ((0 << 3) + 6),
SRAV = ((0 << 3) + 7),
- MOVCI = ((0 << 3) + 1),
JR = ((1 << 3) + 0),
JALR = ((1 << 3) + 1),
@@ -498,14 +502,38 @@
// ----- Coprocessor conditions.
enum FPUCondition {
- F, // False.
- UN, // Unordered.
- EQ, // Equal.
- UEQ, // Unordered or Equal.
- OLT, // Ordered or Less Than.
- ULT, // Unordered or Less Than.
- OLE, // Ordered or Less Than or Equal.
- ULE // Unordered or Less Than or Equal.
+ kNoFPUCondition = -1,
+
+ F = 0, // False.
+ UN = 1, // Unordered.
+ EQ = 2, // Equal.
+ UEQ = 3, // Unordered or Equal.
+ OLT = 4, // Ordered or Less Than.
+ ULT = 5, // Unordered or Less Than.
+ OLE = 6, // Ordered or Less Than or Equal.
+ ULE = 7 // Unordered or Less Than or Equal.
+};
+
+
+// FPU rounding modes.
+enum FPURoundingMode {
+ RN = 0 << 0, // Round to Nearest.
+ RZ = 1 << 0, // Round towards zero.
+ RP = 2 << 0, // Round towards Plus Infinity.
+ RM = 3 << 0, // Round towards Minus Infinity.
+
+ // Aliases.
+ kRoundToNearest = RN,
+ kRoundToZero = RZ,
+ kRoundToPlusInf = RP,
+ kRoundToMinusInf = RM
+};
+
+static const uint32_t kFPURoundingModeMask = 3 << 0;
+
+enum CheckForInexactConversion {
+ kCheckForInexactConversion,
+ kDontCheckForInexactConversion
};
@@ -716,7 +744,7 @@
inline int32_t Imm26Value() const {
ASSERT(InstructionType() == kJumpType);
- return Bits(kImm16Shift + kImm26Bits - 1, kImm26Shift);
+ return Bits(kImm26Shift + kImm26Bits - 1, kImm26Shift);
}
// Say if the instruction should not be used in a branch delay slot.
diff --git a/src/mips/disasm-mips.cc b/src/mips/disasm-mips.cc
index 7df5c41..fde0c58 100644
--- a/src/mips/disasm-mips.cc
+++ b/src/mips/disasm-mips.cc
@@ -112,7 +112,7 @@
void PrintUImm16(Instruction* instr);
void PrintSImm16(Instruction* instr);
void PrintXImm16(Instruction* instr);
- void PrintImm26(Instruction* instr);
+ void PrintXImm26(Instruction* instr);
void PrintCode(Instruction* instr); // For break and trap instructions.
// Printing of instruction name.
void PrintInstructionName(Instruction* instr);
@@ -273,9 +273,9 @@
// Print 26-bit immediate value.
-void Decoder::PrintImm26(Instruction* instr) {
- int32_t imm = instr->Imm26Value();
- out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "%d", imm);
+void Decoder::PrintXImm26(Instruction* instr) {
+ uint32_t imm = instr->Imm26Value() << kImmFieldShift;
+ out_buffer_pos_ += OS::SNPrintF(out_buffer_ + out_buffer_pos_, "0x%x", imm);
}
@@ -383,9 +383,9 @@
}
return 6;
} else {
- ASSERT(STRING_STARTS_WITH(format, "imm26"));
- PrintImm26(instr);
- return 5;
+ ASSERT(STRING_STARTS_WITH(format, "imm26x"));
+ PrintXImm26(instr);
+ return 6;
}
}
case 'r': { // 'r: registers.
@@ -926,10 +926,10 @@
void Decoder::DecodeTypeJump(Instruction* instr) {
switch (instr->OpcodeFieldRaw()) {
case J:
- Format(instr, "j 'imm26");
+ Format(instr, "j 'imm26x");
break;
case JAL:
- Format(instr, "jal 'imm26");
+ Format(instr, "jal 'imm26x");
break;
default:
UNREACHABLE();
@@ -958,6 +958,7 @@
break;
}
default: {
+ Format(instr, "UNSUPPORTED");
UNSUPPORTED_MIPS();
}
}
diff --git a/src/mips/frames-mips.h b/src/mips/frames-mips.h
index 798ef23..2c83893 100644
--- a/src/mips/frames-mips.h
+++ b/src/mips/frames-mips.h
@@ -87,7 +87,6 @@
static const int kNumCalleeSavedFPU = 6;
// Number of registers for which space is reserved in safepoints. Must be a
// multiple of 8.
-// TODO(mips): Only 8 registers may actually be sufficient. Revisit.
static const int kNumSafepointRegisters = 24;
// Define the list of registers actually saved at safepoints.
diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc
index d690ca3..b042a3e 100644
--- a/src/mips/full-codegen-mips.cc
+++ b/src/mips/full-codegen-mips.cc
@@ -2042,9 +2042,8 @@
// Record source position for debugger.
SetSourcePosition(expr->position());
// Call the IC initialization code.
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arg_count, in_loop, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
__ Call(ic, mode, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
@@ -2075,9 +2074,8 @@
// Record source position for debugger.
SetSourcePosition(expr->position());
// Call the IC initialization code.
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count, in_loop);
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count);
__ lw(a2, MemOperand(sp, (arg_count + 1) * kPointerSize)); // Key.
__ Call(ic, RelocInfo::CODE_TARGET, expr->id());
RecordJSReturnSite(expr);
@@ -2098,8 +2096,7 @@
}
// Record source position for debugger.
SetSourcePosition(expr->position());
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, flags);
+ CallFunctionStub stub(arg_count, flags);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -2197,8 +2194,7 @@
}
// Record source position for debugger.
SetSourcePosition(expr->position());
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_IMPLICIT);
+ CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -3574,9 +3570,7 @@
__ li(a2, Operand(expr->name()));
RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arg_count,
- NOT_IN_LOOP,
- mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
__ Call(ic, mode, expr->id());
// Restore context register.
__ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset));
diff --git a/src/mips/ic-mips.cc b/src/mips/ic-mips.cc
index 334dee7..a76c215 100644
--- a/src/mips/ic-mips.cc
+++ b/src/mips/ic-mips.cc
@@ -395,7 +395,6 @@
// Probe the stub cache.
Code::Flags flags = Code::ComputeFlags(kind,
- NOT_IN_LOOP,
MONOMORPHIC,
extra_ic_state,
NORMAL,
@@ -732,9 +731,7 @@
// -----------------------------------
// Probe the stub cache.
- Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC,
- NOT_IN_LOOP,
- MONOMORPHIC);
+ Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC);
Isolate::Current()->stub_cache()->GenerateProbe(
masm, flags, a0, a2, a3, t0, t1);
@@ -1395,10 +1392,8 @@
// -----------------------------------
// Get the receiver from the stack and probe the stub cache.
- Code::Flags flags = Code::ComputeFlags(Code::STORE_IC,
- NOT_IN_LOOP,
- MONOMORPHIC,
- strict_mode);
+ Code::Flags flags =
+ Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC, strict_mode);
Isolate::Current()->stub_cache()->GenerateProbe(
masm, flags, a1, a2, a3, t0, t1);
diff --git a/src/mips/simulator-mips.cc b/src/mips/simulator-mips.cc
index 7628237..17c1897 100644
--- a/src/mips/simulator-mips.cc
+++ b/src/mips/simulator-mips.cc
@@ -33,6 +33,7 @@
#if defined(V8_TARGET_ARCH_MIPS)
+#include "cpu.h"
#include "disasm.h"
#include "assembler.h"
#include "globals.h" // Need the BitCast.
@@ -1215,6 +1216,8 @@
int Simulator::ReadW(int32_t addr, Instruction* instr) {
if (addr >=0 && addr < 0x400) {
// This has to be a NULL-dereference, drop into debugger.
+ PrintF("Memory read from bad address: 0x%08x, pc=0x%08x\n",
+ addr, reinterpret_cast<intptr_t>(instr));
MipsDebugger dbg(this);
dbg.Debug();
}
@@ -1234,6 +1237,8 @@
void Simulator::WriteW(int32_t addr, int value, Instruction* instr) {
if (addr >= 0 && addr < 0x400) {
// This has to be a NULL-dereference, drop into debugger.
+ PrintF("Memory write to bad address: 0x%08x, pc=0x%08x\n",
+ addr, reinterpret_cast<intptr_t>(instr));
MipsDebugger dbg(this);
dbg.Debug();
}
diff --git a/src/objects-debug.cc b/src/objects-debug.cc
index a3dc193..8de7162 100644
--- a/src/objects-debug.cc
+++ b/src/objects-debug.cc
@@ -164,6 +164,9 @@
case JS_PROXY_TYPE:
JSProxy::cast(this)->JSProxyVerify();
break;
+ case JS_FUNCTION_PROXY_TYPE:
+ JSFunctionProxy::cast(this)->JSFunctionProxyVerify();
+ break;
case FOREIGN_TYPE:
Foreign::cast(this)->ForeignVerify();
break;
@@ -536,6 +539,15 @@
VerifyPointer(handler());
}
+
+void JSFunctionProxy::JSFunctionProxyVerify() {
+ ASSERT(IsJSFunctionProxy());
+ JSProxyVerify();
+ VerifyPointer(call_trap());
+ VerifyPointer(construct_trap());
+}
+
+
void Foreign::ForeignVerify() {
ASSERT(IsForeign());
}
diff --git a/src/objects-inl.h b/src/objects-inl.h
index 8a73561..bb24a2f 100644
--- a/src/objects-inl.h
+++ b/src/objects-inl.h
@@ -2881,11 +2881,6 @@
}
-InLoopFlag Code::ic_in_loop() {
- return ExtractICInLoopFromFlags(flags());
-}
-
-
InlineCacheState Code::ic_state() {
InlineCacheState result = ExtractICStateFromFlags(flags());
// Only allow uninitialized or debugger states for non-IC code
@@ -2951,13 +2946,31 @@
bool Code::has_deoptimization_support() {
ASSERT(kind() == FUNCTION);
- return READ_BYTE_FIELD(this, kHasDeoptimizationSupportOffset) == 1;
+ byte flags = READ_BYTE_FIELD(this, kFullCodeFlags);
+ return FullCodeFlagsHasDeoptimizationSupportField::decode(flags);
}
void Code::set_has_deoptimization_support(bool value) {
ASSERT(kind() == FUNCTION);
- WRITE_BYTE_FIELD(this, kHasDeoptimizationSupportOffset, value ? 1 : 0);
+ byte flags = READ_BYTE_FIELD(this, kFullCodeFlags);
+ flags = FullCodeFlagsHasDeoptimizationSupportField::update(flags, value);
+ WRITE_BYTE_FIELD(this, kFullCodeFlags, flags);
+}
+
+
+bool Code::has_debug_break_slots() {
+ ASSERT(kind() == FUNCTION);
+ byte flags = READ_BYTE_FIELD(this, kFullCodeFlags);
+ return FullCodeFlagsHasDebugBreakSlotsField::decode(flags);
+}
+
+
+void Code::set_has_debug_break_slots(bool value) {
+ ASSERT(kind() == FUNCTION);
+ byte flags = READ_BYTE_FIELD(this, kFullCodeFlags);
+ flags = FullCodeFlagsHasDebugBreakSlotsField::update(flags, value);
+ WRITE_BYTE_FIELD(this, kFullCodeFlags, flags);
}
@@ -3091,7 +3104,6 @@
Code::Flags Code::ComputeFlags(Kind kind,
- InLoopFlag in_loop,
InlineCacheState ic_state,
ExtraICState extra_ic_state,
PropertyType type,
@@ -3100,16 +3112,15 @@
// Extra IC state is only allowed for call IC stubs or for store IC
// stubs.
ASSERT(extra_ic_state == kNoExtraICState ||
- (kind == CALL_IC) ||
- (kind == STORE_IC) ||
- (kind == KEYED_STORE_IC));
+ kind == CALL_IC ||
+ kind == STORE_IC ||
+ kind == KEYED_STORE_IC);
// Compute the bit mask.
int bits = KindField::encode(kind)
- | ICInLoopField::encode(in_loop)
| ICStateField::encode(ic_state)
| TypeField::encode(type)
| ExtraICStateField::encode(extra_ic_state)
- | (argc << kFlagsArgumentsCountShift)
+ | (argc << kArgumentsCountShift)
| CacheHolderField::encode(holder);
return static_cast<Flags>(bits);
}
@@ -3119,10 +3130,8 @@
PropertyType type,
ExtraICState extra_ic_state,
InlineCacheHolderFlag holder,
- InLoopFlag in_loop,
int argc) {
- return ComputeFlags(
- kind, in_loop, MONOMORPHIC, extra_ic_state, type, argc, holder);
+ return ComputeFlags(kind, MONOMORPHIC, extra_ic_state, type, argc, holder);
}
@@ -3141,18 +3150,13 @@
}
-InLoopFlag Code::ExtractICInLoopFromFlags(Flags flags) {
- return ICInLoopField::decode(flags);
-}
-
-
PropertyType Code::ExtractTypeFromFlags(Flags flags) {
return TypeField::decode(flags);
}
int Code::ExtractArgumentsCountFromFlags(Flags flags) {
- return (flags & kFlagsArgumentsCountMask) >> kFlagsArgumentsCountShift;
+ return (flags & kArgumentsCountMask) >> kArgumentsCountShift;
}
@@ -3902,7 +3906,16 @@
ACCESSORS(JSProxy, handler, Object, kHandlerOffset)
-ACCESSORS(JSProxy, padding, Object, kPaddingOffset)
+ACCESSORS(JSFunctionProxy, call_trap, Object, kCallTrapOffset)
+ACCESSORS(JSFunctionProxy, construct_trap, Object, kConstructTrapOffset)
+
+
+void JSProxy::InitializeBody(int object_size, Object* value) {
+ ASSERT(!value->IsHeapObject() || !GetHeap()->InNewSpace(value));
+ for (int offset = kHeaderSize; offset < object_size; offset += kPointerSize) {
+ WRITE_FIELD(this, offset, value);
+ }
+}
ACCESSORS(JSWeakMap, table, ObjectHashTable, kTableOffset)
diff --git a/src/objects-printer.cc b/src/objects-printer.cc
index 3573572..0398572 100644
--- a/src/objects-printer.cc
+++ b/src/objects-printer.cc
@@ -151,6 +151,9 @@
case JS_PROXY_TYPE:
JSProxy::cast(this)->JSProxyPrint(out);
break;
+ case JS_FUNCTION_PROXY_TYPE:
+ JSFunctionProxy::cast(this)->JSFunctionProxyPrint(out);
+ break;
case JS_WEAK_MAP_TYPE:
JSWeakMap::cast(this)->JSWeakMapPrint(out);
break;
@@ -588,6 +591,19 @@
}
+void JSFunctionProxy::JSFunctionProxyPrint(FILE* out) {
+ HeapObject::PrintHeader(out, "JSFunctionProxy");
+ PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
+ PrintF(out, " - handler = ");
+ handler()->Print(out);
+ PrintF(out, " - call_trap = ");
+ call_trap()->Print(out);
+ PrintF(out, " - construct_trap = ");
+ construct_trap()->Print(out);
+ PrintF(out, "\n");
+}
+
+
void JSWeakMap::JSWeakMapPrint(FILE* out) {
HeapObject::PrintHeader(out, "JSWeakMap");
PrintF(out, " - map = 0x%p\n", reinterpret_cast<void*>(map()));
diff --git a/src/objects-visiting.cc b/src/objects-visiting.cc
index bde9e83..0aa21dd 100644
--- a/src/objects-visiting.cc
+++ b/src/objects-visiting.cc
@@ -105,6 +105,11 @@
kVisitStructGeneric,
JSProxy::kSize);
+ case JS_FUNCTION_PROXY_TYPE:
+ return GetVisitorIdForSize(kVisitStruct,
+ kVisitStructGeneric,
+ JSFunctionProxy::kSize);
+
case FOREIGN_TYPE:
return GetVisitorIdForSize(kVisitDataObject,
kVisitDataObjectGeneric,
diff --git a/src/objects.cc b/src/objects.cc
index ee997b2..41b4fd4 100644
--- a/src/objects.cc
+++ b/src/objects.cc
@@ -84,7 +84,7 @@
MaybeObject* Object::ToObject() {
- if (IsJSObject()) {
+ if (IsJSReceiver()) {
return this;
} else if (IsNumber()) {
Isolate* isolate = Isolate::Current();
@@ -237,6 +237,7 @@
// Extract trap function.
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("get");
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
+ if (isolate->has_pending_exception()) return Failure::Exception();
if (trap->IsUndefined()) {
// Get the derived `get' property.
trap = isolate->derived_get_trap();
@@ -627,6 +628,7 @@
} else if (heap_object->IsBoolean()) {
holder = global_context->boolean_function()->instance_prototype();
} else if (heap_object->IsJSProxy()) {
+ // TODO(rossberg): do something
return heap->undefined_value(); // For now...
} else {
// Undefined and null have no indexed properties.
@@ -1173,6 +1175,12 @@
HeapNumber::cast(this)->HeapNumberPrint(accumulator);
accumulator->Put('>');
break;
+ case JS_PROXY_TYPE:
+ accumulator->Add("<JSProxy>");
+ break;
+ case JS_FUNCTION_PROXY_TYPE:
+ accumulator->Add("<JSFunctionProxy>");
+ break;
case FOREIGN_TYPE:
accumulator->Add("<Foreign>");
break;
@@ -1251,6 +1259,9 @@
case JS_PROXY_TYPE:
JSProxy::BodyDescriptor::IterateBody(this, v);
break;
+ case JS_FUNCTION_PROXY_TYPE:
+ JSFunctionProxy::BodyDescriptor::IterateBody(this, v);
+ break;
case FOREIGN_TYPE:
reinterpret_cast<Foreign*>(this)->ForeignIterateBody(v);
break;
@@ -2212,6 +2223,7 @@
// Extract trap function.
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("has");
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
+ if (isolate->has_pending_exception()) return Failure::Exception();
if (trap->IsUndefined()) {
trap = isolate->derived_has_trap();
}
@@ -2242,6 +2254,7 @@
// Extract trap function.
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("set");
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
+ if (isolate->has_pending_exception()) return Failure::Exception();
if (trap->IsUndefined()) {
trap = isolate->derived_set_trap();
}
@@ -2269,6 +2282,7 @@
// Extract trap function.
Handle<String> trap_name = isolate->factory()->LookupAsciiSymbol("delete");
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
+ if (isolate->has_pending_exception()) return Failure::Exception();
if (trap->IsUndefined()) {
Handle<Object> args[] = { handler, trap_name };
Handle<Object> error = isolate->factory()->NewTypeError(
@@ -2311,6 +2325,7 @@
Handle<String> trap_name =
isolate->factory()->LookupAsciiSymbol("getPropertyDescriptor");
Handle<Object> trap(v8::internal::GetProperty(handler, trap_name));
+ if (isolate->has_pending_exception()) return NONE;
if (trap->IsUndefined()) {
Handle<Object> args[] = { handler, trap_name };
Handle<Object> error = isolate->factory()->NewTypeError(
@@ -2337,9 +2352,13 @@
HandleScope scope(isolate);
Handle<JSProxy> self(this);
- isolate->factory()->BecomeJSObject(self);
+ if (IsJSFunctionProxy()) {
+ isolate->factory()->BecomeJSFunction(self);
+ // Code will be set on the JavaScript side.
+ } else {
+ isolate->factory()->BecomeJSObject(self);
+ }
ASSERT(self->IsJSObject());
- // TODO(rossberg): recognize function proxies.
}
@@ -7147,7 +7166,6 @@
if (is_inline_cache_stub()) {
PrintF(out, "ic_state = %s\n", ICState2String(ic_state()));
PrintExtraICState(out, kind(), extra_ic_state());
- PrintF(out, "ic_in_loop = %d\n", ic_in_loop() == IN_LOOP);
if (ic_state() == MONOMORPHIC) {
PrintF(out, "type = %s\n", PropertyType2String(type()));
}
diff --git a/src/objects.h b/src/objects.h
index 9dc0771..d9c7a82 100644
--- a/src/objects.h
+++ b/src/objects.h
@@ -3653,7 +3653,6 @@
inline Kind kind();
inline InlineCacheState ic_state(); // Only valid for IC stubs.
inline ExtraICState extra_ic_state(); // Only valid for IC stubs.
- inline InLoopFlag ic_in_loop(); // Only valid for IC stubs.
inline PropertyType type(); // Only valid for monomorphic IC stubs.
inline int arguments_count(); // Only valid for call IC stubs.
@@ -3683,6 +3682,11 @@
inline bool has_deoptimization_support();
inline void set_has_deoptimization_support(bool value);
+ // [has_debug_break_slots]: For FUNCTION kind, tells if it has
+ // been compiled with debug break slots.
+ inline bool has_debug_break_slots();
+ inline void set_has_debug_break_slots(bool value);
+
// [allow_osr_at_loop_nesting_level]: For FUNCTION kind, tells for
// how long the function has been marked for OSR and therefore which
// level of loop nesting we are willing to do on-stack replacement
@@ -3741,7 +3745,6 @@
// Flags operations.
static inline Flags ComputeFlags(
Kind kind,
- InLoopFlag in_loop = NOT_IN_LOOP,
InlineCacheState ic_state = UNINITIALIZED,
ExtraICState extra_ic_state = kNoExtraICState,
PropertyType type = NORMAL,
@@ -3753,11 +3756,9 @@
PropertyType type,
ExtraICState extra_ic_state = kNoExtraICState,
InlineCacheHolderFlag holder = OWN_MAP,
- InLoopFlag in_loop = NOT_IN_LOOP,
int argc = -1);
static inline InlineCacheState ExtractICStateFromFlags(Flags flags);
- static inline InLoopFlag ExtractICInLoopFromFlags(Flags flags);
static inline PropertyType ExtractTypeFromFlags(Flags flags);
static inline Kind ExtractKindFromFlags(Flags flags);
static inline InlineCacheHolderFlag ExtractCacheHolderFromFlags(Flags flags);
@@ -3874,29 +3875,32 @@
static const int kBinaryOpTypeOffset = kStubMajorKeyOffset + 1;
static const int kCompareStateOffset = kStubMajorKeyOffset + 1;
static const int kToBooleanTypeOffset = kStubMajorKeyOffset + 1;
- static const int kHasDeoptimizationSupportOffset = kOptimizableOffset + 1;
+
+ static const int kFullCodeFlags = kOptimizableOffset + 1;
+ class FullCodeFlagsHasDeoptimizationSupportField:
+ public BitField<bool, 0, 1> {}; // NOLINT
+ class FullCodeFlagsHasDebugBreakSlotsField: public BitField<bool, 1, 1> {};
static const int kBinaryOpReturnTypeOffset = kBinaryOpTypeOffset + 1;
- static const int kAllowOSRAtLoopNestingLevelOffset =
- kHasDeoptimizationSupportOffset + 1;
+
+ static const int kAllowOSRAtLoopNestingLevelOffset = kFullCodeFlags + 1;
static const int kSafepointTableOffsetOffset = kStackSlotsOffset + kIntSize;
static const int kStackCheckTableOffsetOffset = kStackSlotsOffset + kIntSize;
// Flags layout. BitField<type, shift, size>.
class ICStateField: public BitField<InlineCacheState, 0, 3> {};
- class ICInLoopField: public BitField<InLoopFlag, 3, 1> {};
- class TypeField: public BitField<PropertyType, 4, 4> {};
- class KindField: public BitField<Kind, 8, 4> {};
- class CacheHolderField: public BitField<InlineCacheHolderFlag, 12, 1> {};
- class ExtraICStateField: public BitField<ExtraICState, 13, 2> {};
+ class TypeField: public BitField<PropertyType, 3, 4> {};
+ class KindField: public BitField<Kind, 7, 4> {};
+ class CacheHolderField: public BitField<InlineCacheHolderFlag, 11, 1> {};
+ class ExtraICStateField: public BitField<ExtraICState, 12, 2> {};
// Signed field cannot be encoded using the BitField class.
- static const int kFlagsArgumentsCountShift = 15;
- static const int kFlagsArgumentsCountMask = 0xffff8000;
+ static const int kArgumentsCountShift = 14;
+ static const int kArgumentsCountMask = ~((1 << kArgumentsCountShift) - 1);
static const int kFlagsNotUsedInLookup =
- ICInLoopField::kMask | TypeField::kMask | CacheHolderField::kMask;
+ TypeField::kMask | CacheHolderField::kMask;
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(Code);
@@ -4731,7 +4735,7 @@
DECL_BOOLEAN_ACCESSORS(has_duplicate_parameters)
// Indicates whether the function is a native function.
- // These needs special threatment in .call and .apply since
+ // These needs special treatment in .call and .apply since
// null passed as the receiver should not be translated to the
// global object.
DECL_BOOLEAN_ACCESSORS(native)
@@ -4998,7 +5002,7 @@
// [prototype_or_initial_map]:
DECL_ACCESSORS(prototype_or_initial_map, Object)
- // [shared_function_info]: The information about the function that
+ // [shared]: The information about the function that
// can be shared by instances.
DECL_ACCESSORS(shared, SharedFunctionInfo)
@@ -6715,9 +6719,6 @@
// [handler]: The handler property.
DECL_ACCESSORS(handler, Object)
- // [padding]: The padding slot (unused, see below).
- DECL_ACCESSORS(padding, Object)
-
// Casting.
static inline JSProxy* cast(Object* obj);
@@ -6741,6 +6742,9 @@
// Turn this into an (empty) JSObject.
void Fix();
+ // Initializes the body after the handler slot.
+ inline void InitializeBody(int object_size, Object* value);
+
// Dispatched behavior.
#ifdef OBJECT_PRINT
inline void JSProxyPrint() {
@@ -6757,9 +6761,11 @@
// upon freeze.
static const int kHandlerOffset = HeapObject::kHeaderSize;
static const int kPaddingOffset = kHandlerOffset + kPointerSize;
- static const int kSize = kPaddingOffset + kPointerSize;
+ static const int kSize = JSObject::kHeaderSize;
+ static const int kHeaderSize = kPaddingOffset;
+ static const int kPaddingSize = kSize - kPaddingOffset;
- STATIC_CHECK(kSize == JSObject::kHeaderSize);
+ STATIC_CHECK(kPaddingSize >= 0);
typedef FixedBodyDescriptor<kHandlerOffset,
kHandlerOffset + kPointerSize,
@@ -6770,12 +6776,41 @@
};
-// TODO(rossberg): Only a stub for now.
class JSFunctionProxy: public JSProxy {
public:
+ // [call_trap]: The call trap.
+ DECL_ACCESSORS(call_trap, Object)
+
+ // [construct_trap]: The construct trap.
+ DECL_ACCESSORS(construct_trap, Object)
+
// Casting.
static inline JSFunctionProxy* cast(Object* obj);
+ // Dispatched behavior.
+#ifdef OBJECT_PRINT
+ inline void JSFunctionProxyPrint() {
+ JSFunctionProxyPrint(stdout);
+ }
+ void JSFunctionProxyPrint(FILE* out);
+#endif
+#ifdef DEBUG
+ void JSFunctionProxyVerify();
+#endif
+
+ // Layout description.
+ static const int kCallTrapOffset = kHandlerOffset + kPointerSize;
+ static const int kConstructTrapOffset = kCallTrapOffset + kPointerSize;
+ static const int kPaddingOffset = kConstructTrapOffset + kPointerSize;
+ static const int kSize = JSFunction::kSize;
+ static const int kPaddingSize = kSize - kPaddingOffset;
+
+ STATIC_CHECK(kPaddingSize >= 0);
+
+ typedef FixedBodyDescriptor<kHandlerOffset,
+ kConstructTrapOffset + kPointerSize,
+ kSize> BodyDescriptor;
+
private:
DISALLOW_IMPLICIT_CONSTRUCTORS(JSFunctionProxy);
};
diff --git a/src/profile-generator-inl.h b/src/profile-generator-inl.h
index 8f4bc6c..88d6e87 100644
--- a/src/profile-generator-inl.h
+++ b/src/profile-generator-inl.h
@@ -78,22 +78,6 @@
}
-void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
- CodeTree::Locator locator;
- tree_.Insert(addr, &locator);
- locator.set_value(CodeEntryInfo(entry, size));
-}
-
-
-void CodeMap::MoveCode(Address from, Address to) {
- tree_.Move(from, to);
-}
-
-void CodeMap::DeleteCode(Address addr) {
- tree_.Remove(addr);
-}
-
-
CodeEntry* ProfileGenerator::EntryForVMState(StateTag tag) {
switch (tag) {
case GC:
diff --git a/src/profile-generator.cc b/src/profile-generator.cc
index 74dfbf4..a7384a6 100644
--- a/src/profile-generator.cc
+++ b/src/profile-generator.cc
@@ -492,6 +492,28 @@
CodeMap::CodeEntryInfo(NULL, 0);
+void CodeMap::AddCode(Address addr, CodeEntry* entry, unsigned size) {
+ DeleteAllCoveredCode(addr, addr + size);
+ CodeTree::Locator locator;
+ tree_.Insert(addr, &locator);
+ locator.set_value(CodeEntryInfo(entry, size));
+}
+
+
+void CodeMap::DeleteAllCoveredCode(Address start, Address end) {
+ List<Address> to_delete;
+ Address addr = end - 1;
+ while (addr >= start) {
+ CodeTree::Locator locator;
+ if (!tree_.FindGreatestLessThan(addr, &locator)) break;
+ Address start2 = locator.key(), end2 = start2 + locator.value().size;
+ if (start2 < end && start < end2) to_delete.Add(start2);
+ addr = start2 - 1;
+ }
+ for (int i = 0; i < to_delete.length(); ++i) tree_.Remove(to_delete[i]);
+}
+
+
CodeEntry* CodeMap::FindEntry(Address addr) {
CodeTree::Locator locator;
if (tree_.FindGreatestLessThan(addr, &locator)) {
@@ -520,6 +542,16 @@
}
+void CodeMap::MoveCode(Address from, Address to) {
+ if (from == to) return;
+ CodeTree::Locator locator;
+ if (!tree_.Find(from, &locator)) return;
+ CodeEntryInfo entry = locator.value();
+ tree_.Remove(from);
+ AddCode(to, entry.entry, entry.size);
+}
+
+
void CodeMap::CodeTreePrinter::Call(
const Address& key, const CodeMap::CodeEntryInfo& value) {
OS::Print("%p %5d %s\n", key, value.size, value.entry->name());
diff --git a/src/profile-generator.h b/src/profile-generator.h
index 6bada36..f3737ea 100644
--- a/src/profile-generator.h
+++ b/src/profile-generator.h
@@ -238,9 +238,8 @@
class CodeMap {
public:
CodeMap() : next_shared_id_(1) { }
- INLINE(void AddCode(Address addr, CodeEntry* entry, unsigned size));
- INLINE(void MoveCode(Address from, Address to));
- INLINE(void DeleteCode(Address addr));
+ void AddCode(Address addr, CodeEntry* entry, unsigned size);
+ void MoveCode(Address from, Address to);
CodeEntry* FindEntry(Address addr);
int GetSharedId(Address addr);
@@ -270,6 +269,8 @@
void Call(const Address& key, const CodeEntryInfo& value);
};
+ void DeleteAllCoveredCode(Address start, Address end);
+
// Fake CodeEntry pointer to distinguish shared function entries.
static CodeEntry* const kSharedFunctionCodeEntry;
diff --git a/src/proxy.js b/src/proxy.js
index 2839159..4e44cd4 100644
--- a/src/proxy.js
+++ b/src/proxy.js
@@ -29,36 +29,6 @@
var $Proxy = global.Proxy
-var fundamentalTraps = [
- "getOwnPropertyDescriptor",
- "getPropertyDescriptor",
- "getOwnPropertyNames",
- "getPropertyNames",
- "defineProperty",
- "delete",
- "fix",
-]
-
-var derivedTraps = [
- "has",
- "hasOwn",
- "get",
- "set",
- "enumerate",
- "keys",
-]
-
-var functionTraps = [
- "callTrap",
- "constructTrap",
-]
-
-$Proxy.createFunction = function(handler, callTrap, constructTrap) {
- handler.callTrap = callTrap
- handler.constructTrap = constructTrap
- $Proxy.create(handler)
-}
-
$Proxy.create = function(handler, proto) {
if (!IS_SPEC_OBJECT(handler))
throw MakeTypeError("handler_non_object", ["create"])
@@ -66,6 +36,20 @@
return %CreateJSProxy(handler, proto)
}
+$Proxy.createFunction = function(handler, callTrap, constructTrap) {
+ if (!IS_SPEC_OBJECT(handler))
+ throw MakeTypeError("handler_non_object", ["create"])
+ if (!IS_SPEC_FUNCTION(callTrap))
+ throw MakeTypeError("trap_function_expected", ["createFunction", "call"])
+ if (IS_UNDEFINED(constructTrap)) {
+ constructTrap = callTrap
+ } else if (!IS_SPEC_FUNCTION(constructTrap)) {
+ throw MakeTypeError("trap_function_expected",
+ ["createFunction", "construct"])
+ }
+ return %CreateJSFunctionProxy(
+ handler, callTrap, constructTrap, $Function.prototype)
+}
@@ -73,6 +57,13 @@
// Builtins
////////////////////////////////////////////////////////////////////////////////
+function DelegateCallAndConstruct(callTrap, constructTrap) {
+ return function() {
+ return %Apply(%_IsConstructCall() ? constructTrap : callTrap,
+ this, arguments, 0, %_ArgumentsLength())
+ }
+}
+
function DerivedGetTrap(receiver, name) {
var desc = this.getPropertyDescriptor(name)
if (IS_UNDEFINED(desc)) { return desc }
diff --git a/src/runtime.cc b/src/runtime.cc
index db2a104..3ea9304 100644
--- a/src/runtime.cc
+++ b/src/runtime.cc
@@ -613,6 +613,19 @@
}
+RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSFunctionProxy) {
+ ASSERT(args.length() == 4);
+ Object* handler = args[0];
+ Object* call_trap = args[1];
+ Object* construct_trap = args[2];
+ Object* prototype = args[3];
+ Object* used_prototype =
+ prototype->IsJSReceiver() ? prototype : isolate->heap()->null_value();
+ return isolate->heap()->AllocateJSFunctionProxy(
+ handler, call_trap, construct_trap, used_prototype);
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSProxy) {
ASSERT(args.length() == 1);
Object* obj = args[0];
@@ -620,6 +633,13 @@
}
+RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSFunctionProxy) {
+ ASSERT(args.length() == 1);
+ Object* obj = args[0];
+ return isolate->heap()->ToBoolean(obj->IsJSFunctionProxy());
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetHandler) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSProxy, proxy, args[0]);
@@ -627,6 +647,20 @@
}
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetCallTrap) {
+ ASSERT(args.length() == 1);
+ CONVERT_CHECKED(JSFunctionProxy, proxy, args[0]);
+ return proxy->call_trap();
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, Runtime_GetConstructTrap) {
+ ASSERT(args.length() == 1);
+ CONVERT_CHECKED(JSFunctionProxy, proxy, args[0]);
+ return proxy->construct_trap();
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_Fix) {
ASSERT(args.length() == 1);
CONVERT_CHECKED(JSProxy, proxy, args[0]);
@@ -2154,7 +2188,6 @@
RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCode) {
- RUNTIME_ASSERT(isolate->bootstrapper()->IsActive());
HandleScope scope(isolate);
ASSERT(args.length() == 2);
@@ -2210,6 +2243,11 @@
// are guaranteed to be in old space.
target->set_literals(*literals, SKIP_WRITE_BARRIER);
target->set_next_function_link(isolate->heap()->undefined_value());
+
+ if (isolate->logger()->is_logging() || CpuProfiler::is_profiling(isolate)) {
+ isolate->logger()->LogExistingFunction(
+ shared, Handle<Code>(shared->code()));
+ }
}
target->set_context(*context);
@@ -5101,6 +5139,7 @@
ASSERT(heap_obj->IsUndefined());
return isolate->heap()->undefined_symbol();
case JS_FUNCTION_TYPE:
+ case JS_FUNCTION_PROXY_TYPE:
return isolate->heap()->function_symbol();
default:
// For any kind of object not handled above, the spec rule for
@@ -8105,15 +8144,9 @@
}
#endif
- // Compile the target function. Here we compile using CompileLazyInLoop in
- // order to get the optimized version. This helps code like delta-blue
- // that calls performance-critical routines through constructors. A
- // constructor call doesn't use a CallIC, it uses a LoadIC followed by a
- // direct call. Since the in-loop tracking takes place through CallICs
- // this means that things called through constructors are never known to
- // be in loops. We compile them as if they are in loops here just in case.
+ // Compile the target function.
ASSERT(!function->is_compiled());
- if (!CompileLazyInLoop(function, KEEP_EXCEPTION)) {
+ if (!CompileLazy(function, KEEP_EXCEPTION)) {
return Failure::Exception();
}
@@ -8127,6 +8160,15 @@
HandleScope scope(isolate);
ASSERT(args.length() == 1);
Handle<JSFunction> function = args.at<JSFunction>(0);
+
+ // If the function is not compiled ignore the lazy
+ // recompilation. This can happen if the debugger is activated and
+ // the function is returned to the not compiled state.
+ if (!function->shared()->is_compiled()) {
+ function->ReplaceCode(function->shared()->code());
+ return function->code();
+ }
+
// If the function is not optimizable or debugger is active continue using the
// code from the full compiler.
if (!function->shared()->code()->optimizable() ||
@@ -8192,8 +8234,6 @@
if (type == Deoptimizer::EAGER) {
RUNTIME_ASSERT(function->IsOptimized());
- } else {
- RUNTIME_ASSERT(!function->IsOptimized());
}
// Avoid doing too much work when running with --always-opt and keep
@@ -8212,8 +8252,6 @@
it.Advance();
}
- // TODO(kasperl): For now, we cannot support removing the optimized
- // code when we have recursive invocations of the same function.
if (activations == 0) {
if (FLAG_trace_deopt) {
PrintF("[removing optimized code for: ");
@@ -8221,6 +8259,8 @@
PrintF("]\n");
}
function->ReplaceCode(function->shared()->code());
+ } else {
+ Deoptimizer::DeoptimizeFunction(*function);
}
return isolate->heap()->undefined_value();
}
@@ -8407,6 +8447,49 @@
}
+RUNTIME_FUNCTION(MaybeObject*, Runtime_Apply) {
+ HandleScope scope(isolate);
+ ASSERT(args.length() == 5);
+ CONVERT_CHECKED(JSReceiver, fun, args[0]);
+ Object* receiver = args[1];
+ CONVERT_CHECKED(JSObject, arguments, args[2]);
+ CONVERT_CHECKED(Smi, shift, args[3]);
+ CONVERT_CHECKED(Smi, arity, args[4]);
+
+ int offset = shift->value();
+ int argc = arity->value();
+ ASSERT(offset >= 0);
+ ASSERT(argc >= 0);
+
+ // If there are too many arguments, allocate argv via malloc.
+ const int argv_small_size = 10;
+ Handle<Object> argv_small_buffer[argv_small_size];
+ SmartArrayPointer<Handle<Object> > argv_large_buffer;
+ Handle<Object>* argv = argv_small_buffer;
+ if (argc > argv_small_size) {
+ argv = new Handle<Object>[argc];
+ if (argv == NULL) return isolate->StackOverflow();
+ argv_large_buffer = SmartArrayPointer<Handle<Object> >(argv);
+ }
+
+ for (int i = 0; i < argc; ++i) {
+ MaybeObject* maybe = arguments->GetElement(offset + i);
+ Object* object;
+ if (!maybe->To<Object>(&object)) return maybe;
+ argv[i] = Handle<Object>(object);
+ }
+
+ bool threw = false;
+ Handle<JSReceiver> hfun(fun);
+ Handle<Object> hreceiver(receiver);
+ Handle<Object> result = Execution::Call(
+ hfun, hreceiver, argc, reinterpret_cast<Object***>(argv), &threw, true);
+
+ if (threw) return Failure::Exception();
+ return *result;
+}
+
+
RUNTIME_FUNCTION(MaybeObject*, Runtime_GetFunctionDelegate) {
HandleScope scope(isolate);
ASSERT(args.length() == 1);
@@ -10951,15 +11034,16 @@
at_local_(false) {
// Check whether the first scope is actually a local scope.
- if (context_->IsGlobalContext()) {
- // If there is a stack slot for .result then this local scope has been
- // created for evaluating top level code and it is not a real local scope.
- // Checking for the existence of .result seems fragile, but the scope info
- // saved with the code object does not otherwise have that information.
- int index = function_->shared()->scope_info()->
- StackSlotIndex(isolate_->heap()->result_symbol());
- at_local_ = index < 0;
- } else if (context_->IsFunctionContext()) {
+ // If there is a stack slot for .result then this local scope has been
+ // created for evaluating top level code and it is not a real local scope.
+ // Checking for the existence of .result seems fragile, but the scope info
+ // saved with the code object does not otherwise have that information.
+ int index = function_->shared()->scope_info()->
+ StackSlotIndex(isolate_->heap()->result_symbol());
+ if (index >= 0) {
+ local_done_ = true;
+ } else if (context_->IsGlobalContext() ||
+ context_->IsFunctionContext()) {
at_local_ = true;
} else if (context_->closure() != *function_) {
// The context_ is a block or with or catch block from the outer function.
@@ -11006,7 +11090,7 @@
}
// Return the type of the current scope.
- int Type() {
+ ScopeType Type() {
if (at_local_) {
return ScopeTypeLocal;
}
diff --git a/src/runtime.h b/src/runtime.h
index ddd5295..1538b7d 100644
--- a/src/runtime.h
+++ b/src/runtime.h
@@ -80,6 +80,7 @@
\
/* Utilities */ \
F(CheckIsBootstrapping, 0, 1) \
+ F(Apply, 5, 1) \
F(GetFunctionDelegate, 1, 1) \
F(GetConstructorDelegate, 1, 1) \
F(NewArgumentsFast, 3, 1) \
@@ -287,8 +288,12 @@
\
/* Harmony proxies */ \
F(CreateJSProxy, 2, 1) \
+ F(CreateJSFunctionProxy, 4, 1) \
F(IsJSProxy, 1, 1) \
+ F(IsJSFunctionProxy, 1, 1) \
F(GetHandler, 1, 1) \
+ F(GetCallTrap, 1, 1) \
+ F(GetConstructTrap, 1, 1) \
F(Fix, 1, 1) \
\
/* Harmony weakmaps */ \
diff --git a/src/runtime.js b/src/runtime.js
index 61deb9b..14ff1b6 100644
--- a/src/runtime.js
+++ b/src/runtime.js
@@ -366,7 +366,7 @@
// an expensive ToBoolean conversion in the generated code.
function INSTANCE_OF(F) {
var V = this;
- if (!IS_FUNCTION(F)) {
+ if (!IS_SPEC_FUNCTION(F)) {
throw %MakeTypeError('instanceof_function_expected', [V]);
}
@@ -408,7 +408,7 @@
if (!IS_FUNCTION(delegate)) {
throw %MakeTypeError('called_non_callable', [typeof this]);
}
- return delegate.apply(this, arguments);
+ return %Apply(delegate, this, arguments, 0, %_ArgumentsLength());
}
@@ -417,7 +417,32 @@
if (!IS_FUNCTION(delegate)) {
throw %MakeTypeError('called_non_callable', [typeof this]);
}
- return delegate.apply(this, arguments);
+ return %Apply(delegate, this, arguments, 0, %_ArgumentsLength());
+}
+
+
+function CALL_FUNCTION_PROXY() {
+ var arity = %_ArgumentsLength() - 1;
+ var proxy = %_Arguments(arity); // The proxy comes in as an additional arg.
+ var trap = %GetCallTrap(proxy);
+ return %Apply(trap, this, arguments, 0, arity);
+}
+
+
+function CALL_FUNCTION_PROXY_AS_CONSTRUCTOR(proxy) {
+ var arity = %_ArgumentsLength() - 1;
+ var trap = %GetConstructTrap(proxy);
+ var receiver = void 0;
+ if (!IS_UNDEFINED(trap)) {
+ trap = %GetCallTrap(proxy);
+ var proto = proxy.prototype;
+ if (!IS_SPEC_OBJECT(proto) && proto !== null) {
+ throw MakeTypeError("proto_object_or_null", [proto]);
+ }
+ receiver = new global.Object();
+ receiver.__proto__ = proto;
+ }
+ return %Apply(trap, this, arguments, 1, arity);
}
@@ -428,7 +453,8 @@
// that takes care of more eventualities.
if (IS_ARRAY(args)) {
length = args.length;
- if (%_IsSmi(length) && length >= 0 && length < 0x800000 && IS_FUNCTION(this)) {
+ if (%_IsSmi(length) && length >= 0 && length < 0x800000 &&
+ IS_SPEC_FUNCTION(this)) {
return length;
}
}
@@ -442,7 +468,7 @@
throw %MakeRangeError('stack_overflow', []);
}
- if (!IS_FUNCTION(this)) {
+ if (!IS_SPEC_FUNCTION(this)) {
throw %MakeTypeError('apply_non_function', [ %ToString(this), typeof this ]);
}
@@ -610,13 +636,13 @@
// ECMA-262, section 8.6.2.6, page 28.
function DefaultNumber(x) {
var valueOf = x.valueOf;
- if (IS_FUNCTION(valueOf)) {
+ if (IS_SPEC_FUNCTION(valueOf)) {
var v = %_CallFunction(x, valueOf);
if (%IsPrimitive(v)) return v;
}
var toString = x.toString;
- if (IS_FUNCTION(toString)) {
+ if (IS_SPEC_FUNCTION(toString)) {
var s = %_CallFunction(x, toString);
if (%IsPrimitive(s)) return s;
}
@@ -628,13 +654,13 @@
// ECMA-262, section 8.6.2.6, page 28.
function DefaultString(x) {
var toString = x.toString;
- if (IS_FUNCTION(toString)) {
+ if (IS_SPEC_FUNCTION(toString)) {
var s = %_CallFunction(x, toString);
if (%IsPrimitive(s)) return s;
}
var valueOf = x.valueOf;
- if (IS_FUNCTION(valueOf)) {
+ if (IS_SPEC_FUNCTION(valueOf)) {
var v = %_CallFunction(x, valueOf);
if (%IsPrimitive(v)) return v;
}
diff --git a/src/scopes.cc b/src/scopes.cc
index e101fb7..d5a7a9f 100644
--- a/src/scopes.cc
+++ b/src/scopes.cc
@@ -695,7 +695,7 @@
PrintName(var->name());
PrintF("; // ");
PrintLocation(var);
- if (var->is_accessed_from_inner_function_scope()) {
+ if (var->is_accessed_from_inner_scope()) {
if (!var->IsUnallocated()) PrintF(", ");
PrintF("inner scope access");
}
@@ -818,7 +818,7 @@
// another variable that is introduced dynamically via an 'eval' call
// or a 'with' statement).
Variable* Scope::LookupRecursive(Handle<String> name,
- bool from_inner_function,
+ bool from_inner_scope,
Variable** invalidated_local) {
// If we find a variable, but the current scope calls 'eval', the found
// variable may not be the correct one (the 'eval' may introduce a
@@ -834,7 +834,7 @@
// (Even if there is an 'eval' in this scope which introduces the
// same variable again, the resulting variable remains the same.
// Note that enclosing 'with' statements are handled at the call site.)
- if (!from_inner_function)
+ if (!from_inner_scope)
return var;
} else {
@@ -850,10 +850,7 @@
var = function_->var();
} else if (outer_scope_ != NULL) {
- var = outer_scope_->LookupRecursive(
- name,
- is_function_scope() || from_inner_function,
- invalidated_local);
+ var = outer_scope_->LookupRecursive(name, true, invalidated_local);
// We may have found a variable in an outer scope. However, if
// the current scope is inside a 'with', the actual variable may
// be a property introduced via the 'with' statement. Then, the
@@ -870,8 +867,8 @@
ASSERT(var != NULL);
// If this is a lookup from an inner scope, mark the variable.
- if (from_inner_function) {
- var->MarkAsAccessedFromInnerFunctionScope();
+ if (from_inner_scope) {
+ var->MarkAsAccessedFromInnerScope();
}
// If the variable we have found is just a guess, invalidate the
@@ -1022,7 +1019,7 @@
// via an eval() call. This is only possible if the variable has a
// visible name.
if ((var->is_this() || var->name()->length() > 0) &&
- (var->is_accessed_from_inner_function_scope() ||
+ (var->is_accessed_from_inner_scope() ||
scope_calls_eval_ ||
inner_scope_calls_eval_ ||
scope_contains_with_ ||
@@ -1045,7 +1042,7 @@
// catch-bound variables are always allocated in a context.
if (var->mode() == Variable::TEMPORARY) return false;
if (is_catch_scope() || is_block_scope()) return true;
- return var->is_accessed_from_inner_function_scope() ||
+ return var->is_accessed_from_inner_scope() ||
scope_calls_eval_ ||
inner_scope_calls_eval_ ||
scope_contains_with_ ||
@@ -1111,7 +1108,7 @@
if (uses_nonstrict_arguments) {
// Give the parameter a use from an inner scope, to force allocation
// to the context.
- var->MarkAsAccessedFromInnerFunctionScope();
+ var->MarkAsAccessedFromInnerScope();
}
if (MustAllocate(var)) {
diff --git a/src/string.js b/src/string.js
index 6f68ce0..297105d 100644
--- a/src/string.js
+++ b/src/string.js
@@ -223,7 +223,7 @@
// Delegate to one of the regular expression variants if necessary.
if (IS_REGEXP(search)) {
%_Log('regexp', 'regexp-replace,%0r,%1S', [search, subject]);
- if (IS_FUNCTION(replace)) {
+ if (IS_SPEC_FUNCTION(replace)) {
if (search.global) {
return StringReplaceGlobalRegExpWithFunction(subject, search, replace);
} else {
@@ -250,7 +250,7 @@
builder.addSpecialSlice(0, start);
// Compute the string to replace with.
- if (IS_FUNCTION(replace)) {
+ if (IS_SPEC_FUNCTION(replace)) {
var receiver = %GetDefaultReceiver(replace);
builder.add(%_CallFunction(receiver,
search,
@@ -440,13 +440,14 @@
i++;
}
} else {
+ var receiver = %GetDefaultReceiver(replace);
while (i < len) {
var elem = res[i];
if (!%_IsSmi(elem)) {
// elem must be an Array.
// Use the apply argument as backing for global RegExp properties.
lastMatchInfoOverride = elem;
- var func_result = replace.apply(null, elem);
+ var func_result = %Apply(replace, receiver, elem, 0, elem.length);
res[i] = TO_STRING_INLINE(func_result);
}
i++;
@@ -472,11 +473,11 @@
// The number of captures plus one for the match.
var m = NUMBER_OF_CAPTURES(matchInfo) >> 1;
var replacement;
+ var receiver = %GetDefaultReceiver(replace);
if (m == 1) {
// No captures, only the match, which is always valid.
var s = SubString(subject, index, endOfMatch);
// Don't call directly to avoid exposing the built-in global object.
- var receiver = %GetDefaultReceiver(replace);
replacement =
%_CallFunction(receiver, s, index, subject, replace);
} else {
@@ -487,7 +488,7 @@
parameters[j] = index;
parameters[j + 1] = subject;
- replacement = replace.apply(null, parameters);
+ replacement = %Apply(replace, receiver, parameters, 0, j + 2);
}
result.add(replacement); // The add method converts to string if necessary.
diff --git a/src/stub-cache.cc b/src/stub-cache.cc
index 404da44..5596330 100644
--- a/src/stub-cache.cc
+++ b/src/stub-cache.cc
@@ -652,7 +652,6 @@
(kind == Code::CALL_IC ? Logger::type : Logger::KEYED_##type)
MaybeObject* StubCache::ComputeCallConstant(int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state,
String* name,
@@ -678,7 +677,6 @@
CONSTANT_FUNCTION,
extra_ic_state,
cache_holder,
- in_loop,
argc);
Object* code = map_holder->map()->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
@@ -688,8 +686,7 @@
// caches.
if (!function->is_compiled()) return Failure::InternalError();
// Compile the stub - only create stubs for fully compiled functions.
- CallStubCompiler compiler(
- argc, in_loop, kind, extra_ic_state, cache_holder);
+ CallStubCompiler compiler(argc, kind, extra_ic_state, cache_holder);
{ MaybeObject* maybe_code =
compiler.CompileCallConstant(object, holder, function, name, check);
if (!maybe_code->ToObject(&code)) return maybe_code;
@@ -711,7 +708,6 @@
MaybeObject* StubCache::ComputeCallField(int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state,
String* name,
@@ -734,12 +730,10 @@
FIELD,
extra_ic_state,
cache_holder,
- in_loop,
argc);
Object* code = map_holder->map()->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
- CallStubCompiler compiler(
- argc, in_loop, kind, extra_ic_state, cache_holder);
+ CallStubCompiler compiler(argc, kind, extra_ic_state, cache_holder);
{ MaybeObject* maybe_code =
compiler.CompileCallField(JSObject::cast(object),
holder,
@@ -785,12 +779,10 @@
INTERCEPTOR,
extra_ic_state,
cache_holder,
- NOT_IN_LOOP,
argc);
Object* code = map_holder->map()->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
- CallStubCompiler compiler(
- argc, NOT_IN_LOOP, kind, extra_ic_state, cache_holder);
+ CallStubCompiler compiler(argc, kind, extra_ic_state, cache_holder);
{ MaybeObject* maybe_code =
compiler.CompileCallInterceptor(JSObject::cast(object), holder, name);
if (!maybe_code->ToObject(&code)) return maybe_code;
@@ -811,14 +803,12 @@
MaybeObject* StubCache::ComputeCallNormal(int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state,
String* name,
JSObject* receiver) {
Object* code;
- { MaybeObject* maybe_code =
- ComputeCallNormal(argc, in_loop, kind, extra_ic_state);
+ { MaybeObject* maybe_code = ComputeCallNormal(argc, kind, extra_ic_state);
if (!maybe_code->ToObject(&code)) return maybe_code;
}
return code;
@@ -826,7 +816,6 @@
MaybeObject* StubCache::ComputeCallGlobal(int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state,
String* name,
@@ -841,7 +830,6 @@
NORMAL,
extra_ic_state,
cache_holder,
- in_loop,
argc);
Object* code = map_holder->map()->FindInCodeCache(name, flags);
if (code->IsUndefined()) {
@@ -850,8 +838,7 @@
// internal error which will make sure we do not update any
// caches.
if (!function->is_compiled()) return Failure::InternalError();
- CallStubCompiler compiler(
- argc, in_loop, kind, extra_ic_state, cache_holder);
+ CallStubCompiler compiler(argc, kind, extra_ic_state, cache_holder);
{ MaybeObject* maybe_code =
compiler.CompileCallGlobal(receiver, holder, cell, function, name);
if (!maybe_code->ToObject(&code)) return maybe_code;
@@ -920,14 +907,12 @@
Code* StubCache::FindCallInitialize(int argc,
- InLoopFlag in_loop,
RelocInfo::Mode mode,
Code::Kind kind) {
Code::ExtraICState extra_state =
CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) |
CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT);
Code::Flags flags = Code::ComputeFlags(kind,
- in_loop,
UNINITIALIZED,
extra_state,
NORMAL,
@@ -941,14 +926,12 @@
MaybeObject* StubCache::ComputeCallInitialize(int argc,
- InLoopFlag in_loop,
RelocInfo::Mode mode,
Code::Kind kind) {
Code::ExtraICState extra_state =
CallICBase::StringStubState::encode(DEFAULT_STRING_STUB) |
CallICBase::Contextual::encode(mode == RelocInfo::CODE_TARGET_CONTEXT);
Code::Flags flags = Code::ComputeFlags(kind,
- in_loop,
UNINITIALIZED,
extra_state,
NORMAL,
@@ -964,49 +947,26 @@
Handle<Code> StubCache::ComputeCallInitialize(int argc,
- InLoopFlag in_loop,
RelocInfo::Mode mode) {
- if (in_loop == IN_LOOP) {
- // Force the creation of the corresponding stub outside loops,
- // because it may be used when clearing the ICs later - it is
- // possible for a series of IC transitions to lose the in-loop
- // information, and the IC clearing code can't generate a stub
- // that it needs so we need to ensure it is generated already.
- ComputeCallInitialize(argc, NOT_IN_LOOP, mode);
- }
CALL_HEAP_FUNCTION(isolate_,
- ComputeCallInitialize(argc, in_loop, mode, Code::CALL_IC),
+ ComputeCallInitialize(argc, mode, Code::CALL_IC),
Code);
}
-Handle<Code> StubCache::ComputeKeyedCallInitialize(int argc,
- InLoopFlag in_loop) {
- if (in_loop == IN_LOOP) {
- // Force the creation of the corresponding stub outside loops,
- // because it may be used when clearing the ICs later - it is
- // possible for a series of IC transitions to lose the in-loop
- // information, and the IC clearing code can't generate a stub
- // that it needs so we need to ensure it is generated already.
- ComputeKeyedCallInitialize(argc, NOT_IN_LOOP);
- }
+Handle<Code> StubCache::ComputeKeyedCallInitialize(int argc) {
CALL_HEAP_FUNCTION(
isolate_,
- ComputeCallInitialize(argc,
- in_loop,
- RelocInfo::CODE_TARGET,
- Code::KEYED_CALL_IC),
+ ComputeCallInitialize(argc, RelocInfo::CODE_TARGET, Code::KEYED_CALL_IC),
Code);
}
MaybeObject* StubCache::ComputeCallPreMonomorphic(
int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state) {
Code::Flags flags = Code::ComputeFlags(kind,
- in_loop,
PREMONOMORPHIC,
extra_ic_state,
NORMAL,
@@ -1022,11 +982,9 @@
MaybeObject* StubCache::ComputeCallNormal(int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state) {
Code::Flags flags = Code::ComputeFlags(kind,
- in_loop,
MONOMORPHIC,
extra_ic_state,
NORMAL,
@@ -1041,12 +999,9 @@
}
-MaybeObject* StubCache::ComputeCallArguments(int argc,
- InLoopFlag in_loop,
- Code::Kind kind) {
+MaybeObject* StubCache::ComputeCallArguments(int argc, Code::Kind kind) {
ASSERT(kind == Code::KEYED_CALL_IC);
Code::Flags flags = Code::ComputeFlags(kind,
- in_loop,
MEGAMORPHIC,
Code::kNoExtraICState,
NORMAL,
@@ -1063,11 +1018,9 @@
MaybeObject* StubCache::ComputeCallMegamorphic(
int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state) {
Code::Flags flags = Code::ComputeFlags(kind,
- in_loop,
MEGAMORPHIC,
extra_ic_state,
NORMAL,
@@ -1088,7 +1041,6 @@
// MONOMORPHIC_PROTOTYPE_FAILURE state is used to make sure that miss stubs
// and monomorphic stubs are not mixed up together in the stub cache.
Code::Flags flags = Code::ComputeFlags(kind,
- NOT_IN_LOOP,
MONOMORPHIC_PROTOTYPE_FAILURE,
extra_ic_state,
NORMAL,
@@ -1111,7 +1063,6 @@
// Extra IC state is irrelevant for debug break ICs. They jump to
// the actual call ic to carry out the work.
Code::Flags flags = Code::ComputeFlags(kind,
- NOT_IN_LOOP,
DEBUG_BREAK,
Code::kNoExtraICState,
NORMAL,
@@ -1132,7 +1083,6 @@
// Extra IC state is irrelevant for debug break ICs. They jump to
// the actual call ic to carry out the work.
Code::Flags flags = Code::ComputeFlags(kind,
- NOT_IN_LOOP,
DEBUG_PREPARE_STEP_IN,
Code::kNoExtraICState,
NORMAL,
@@ -1672,7 +1622,7 @@
String* name,
InlineCacheState state) {
Code::Flags flags = Code::ComputeFlags(
- Code::KEYED_LOAD_IC, NOT_IN_LOOP, state, Code::kNoExtraICState, type);
+ Code::KEYED_LOAD_IC, state, Code::kNoExtraICState, type);
MaybeObject* result = GetCodeWithFlags(flags, name);
if (!result->IsFailure()) {
PROFILE(isolate(),
@@ -1688,8 +1638,8 @@
MaybeObject* StoreStubCompiler::GetCode(PropertyType type, String* name) {
- Code::Flags flags = Code::ComputeMonomorphicFlags(
- Code::STORE_IC, type, strict_mode_);
+ Code::Flags flags =
+ Code::ComputeMonomorphicFlags(Code::STORE_IC, type, strict_mode_);
MaybeObject* result = GetCodeWithFlags(flags, name);
if (!result->IsFailure()) {
PROFILE(isolate(),
@@ -1707,8 +1657,8 @@
MaybeObject* KeyedStoreStubCompiler::GetCode(PropertyType type,
String* name,
InlineCacheState state) {
- Code::Flags flags = Code::ComputeFlags(
- Code::KEYED_STORE_IC, NOT_IN_LOOP, state, strict_mode_, type);
+ Code::Flags flags =
+ Code::ComputeFlags(Code::KEYED_STORE_IC, state, strict_mode_, type);
MaybeObject* result = GetCodeWithFlags(flags, name);
if (!result->IsFailure()) {
PROFILE(isolate(),
@@ -1730,12 +1680,10 @@
CallStubCompiler::CallStubCompiler(int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state,
InlineCacheHolderFlag cache_holder)
: arguments_(argc),
- in_loop_(in_loop),
kind_(kind),
extra_ic_state_(extra_ic_state),
cache_holder_(cache_holder) {
@@ -1796,7 +1744,6 @@
type,
extra_ic_state_,
cache_holder_,
- in_loop_,
argc);
return GetCodeWithFlags(flags, name);
}
diff --git a/src/stub-cache.h b/src/stub-cache.h
index f21015a..18c157b 100644
--- a/src/stub-cache.h
+++ b/src/stub-cache.h
@@ -194,7 +194,6 @@
MUST_USE_RESULT MaybeObject* ComputeCallField(
int argc,
- InLoopFlag in_loop,
Code::Kind,
Code::ExtraICState extra_ic_state,
String* name,
@@ -204,7 +203,6 @@
MUST_USE_RESULT MaybeObject* ComputeCallConstant(
int argc,
- InLoopFlag in_loop,
Code::Kind,
Code::ExtraICState extra_ic_state,
String* name,
@@ -214,7 +212,6 @@
MUST_USE_RESULT MaybeObject* ComputeCallNormal(
int argc,
- InLoopFlag in_loop,
Code::Kind,
Code::ExtraICState extra_ic_state,
String* name,
@@ -230,7 +227,6 @@
MUST_USE_RESULT MaybeObject* ComputeCallGlobal(
int argc,
- InLoopFlag in_loop,
Code::Kind,
Code::ExtraICState extra_ic_state,
String* name,
@@ -242,33 +238,27 @@
// ---
MUST_USE_RESULT MaybeObject* ComputeCallInitialize(int argc,
- InLoopFlag in_loop,
RelocInfo::Mode mode,
Code::Kind kind);
Handle<Code> ComputeCallInitialize(int argc,
- InLoopFlag in_loop,
RelocInfo::Mode mode);
- Handle<Code> ComputeKeyedCallInitialize(int argc, InLoopFlag in_loop);
+ Handle<Code> ComputeKeyedCallInitialize(int argc);
MUST_USE_RESULT MaybeObject* ComputeCallPreMonomorphic(
int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state);
MUST_USE_RESULT MaybeObject* ComputeCallNormal(int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState state);
MUST_USE_RESULT MaybeObject* ComputeCallArguments(int argc,
- InLoopFlag in_loop,
Code::Kind kind);
MUST_USE_RESULT MaybeObject* ComputeCallMegamorphic(int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState state);
@@ -278,7 +268,6 @@
// Finds the Code object stored in the Heap::non_monomorphic_cache().
MUST_USE_RESULT Code* FindCallInitialize(int argc,
- InLoopFlag in_loop,
RelocInfo::Mode mode,
Code::Kind kind);
@@ -379,11 +368,7 @@
// Use the seed from the primary cache in the secondary cache.
uint32_t string_low32bits =
static_cast<uint32_t>(reinterpret_cast<uintptr_t>(name));
- // We always set the in_loop bit to zero when generating the lookup code
- // so do it here too so the hash codes match.
- uint32_t iflags =
- (static_cast<uint32_t>(flags) & ~Code::ICInLoopField::kMask);
- uint32_t key = seed - string_low32bits + iflags;
+ uint32_t key = seed - string_low32bits + flags;
return key & ((kSecondaryTableSize - 1) << kHeapObjectTagSize);
}
@@ -755,7 +740,6 @@
class CallStubCompiler: public StubCompiler {
public:
CallStubCompiler(int argc,
- InLoopFlag in_loop,
Code::Kind kind,
Code::ExtraICState extra_ic_state,
InlineCacheHolderFlag cache_holder);
@@ -815,7 +799,6 @@
String* name);
const ParameterCount arguments_;
- const InLoopFlag in_loop_;
const Code::Kind kind_;
const Code::ExtraICState extra_ic_state_;
const InlineCacheHolderFlag cache_holder_;
diff --git a/src/type-info.cc b/src/type-info.cc
index bdf7bc3..c64368e 100644
--- a/src/type-info.cc
+++ b/src/type-info.cc
@@ -190,7 +190,6 @@
NORMAL,
extra_ic_state,
OWN_MAP,
- NOT_IN_LOOP,
arity);
CollectReceiverTypes(expr->id(), name, flags, types);
}
diff --git a/src/v8globals.h b/src/v8globals.h
index 8be5db1..eb5c49d 100644
--- a/src/v8globals.h
+++ b/src/v8globals.h
@@ -302,12 +302,6 @@
};
-enum InLoopFlag {
- NOT_IN_LOOP,
- IN_LOOP
-};
-
-
enum CallFunctionFlags {
NO_CALL_FUNCTION_FLAGS = 0,
// Receiver might implicitly be the global objects. If it is, the
diff --git a/src/v8natives.js b/src/v8natives.js
index 1616ac3..588bdb2 100644
--- a/src/v8natives.js
+++ b/src/v8natives.js
@@ -298,7 +298,7 @@
if (receiver == null && !IS_UNDETECTABLE(receiver)) {
receiver = %GlobalReceiver(global);
}
- if (!IS_FUNCTION(fun)) {
+ if (!IS_SPEC_FUNCTION(fun)) {
throw new $TypeError('Object.prototype.__defineGetter__: Expecting function');
}
var desc = new PropertyDescriptor();
@@ -323,7 +323,7 @@
if (receiver == null && !IS_UNDETECTABLE(receiver)) {
receiver = %GlobalReceiver(global);
}
- if (!IS_FUNCTION(fun)) {
+ if (!IS_SPEC_FUNCTION(fun)) {
throw new $TypeError(
'Object.prototype.__defineSetter__: Expecting function');
}
@@ -453,7 +453,7 @@
if ("get" in obj) {
var get = obj.get;
- if (!IS_UNDEFINED(get) && !IS_FUNCTION(get)) {
+ if (!IS_UNDEFINED(get) && !IS_SPEC_FUNCTION(get)) {
throw MakeTypeError("getter_must_be_callable", [get]);
}
desc.setGet(get);
@@ -461,7 +461,7 @@
if ("set" in obj) {
var set = obj.set;
- if (!IS_UNDEFINED(set) && !IS_FUNCTION(set)) {
+ if (!IS_UNDEFINED(set) && !IS_SPEC_FUNCTION(set)) {
throw MakeTypeError("setter_must_be_callable", [set]);
}
desc.setSet(set);
@@ -623,7 +623,7 @@
throw MakeTypeError("handler_trap_missing", [handler, name]);
}
trap = defaultTrap;
- } else if (!IS_FUNCTION(trap)) {
+ } else if (!IS_SPEC_FUNCTION(trap)) {
throw MakeTypeError("handler_trap_must_be_callable", [handler, name]);
}
return trap;
@@ -977,7 +977,7 @@
// Clone the attributes object for protection.
// TODO(rossberg): not spec'ed yet, so not sure if this should involve
// non-own properties as it does (or non-enumerable ones, as it doesn't?).
- var attributesClone = {}
+ var attributesClone = {};
for (var a in attributes) {
attributesClone[a] = attributes[a];
}
@@ -1041,7 +1041,16 @@
if (IS_UNDEFINED(props)) {
throw MakeTypeError("handler_returned_undefined", [handler, "fix"]);
}
- %Fix(obj);
+
+ if (IS_SPEC_FUNCTION(obj)) {
+ var callTrap = %GetCallTrap(obj);
+ var constructTrap = %GetConstructTrap(obj);
+ var code = DelegateCallAndConstruct(callTrap, constructTrap);
+ %Fix(obj); // becomes a regular function
+ %SetCode(obj, code);
+ } else {
+ %Fix(obj);
+ }
ObjectDefineProperties(obj, props);
}
@@ -1412,6 +1421,10 @@
$Function.prototype.constructor = $Function;
function FunctionSourceString(func) {
+ while (%IsJSFunctionProxy(func)) {
+ func = %GetCallTrap(func);
+ }
+
if (!IS_FUNCTION(func)) {
throw new $TypeError('Function.prototype.toString is not generic');
}
@@ -1441,12 +1454,13 @@
// ES5 15.3.4.5
function FunctionBind(this_arg) { // Length is 1.
- if (!IS_FUNCTION(this)) {
+ if (!IS_SPEC_FUNCTION(this)) {
throw new $TypeError('Bind must be called on a function');
}
// this_arg is not an argument that should be bound.
var argc_bound = (%_ArgumentsLength() || 1) - 1;
var fn = this;
+
if (argc_bound == 0) {
var result = function() {
if (%_IsConstructCall()) {
@@ -1455,8 +1469,7 @@
// materializing it and guarantee that this function will be optimized.
return %NewObjectFromBound(fn, null);
}
-
- return fn.apply(this_arg, arguments);
+ return %Apply(fn, this_arg, arguments, 0, %_ArgumentsLength());
};
} else {
var bound_args = new InternalArray(argc_bound);
@@ -1486,7 +1499,7 @@
for (var i = 0; i < argc; i++) {
args[argc_bound + i] = %_Arguments(i);
}
- return fn.apply(this_arg, args);
+ return %Apply(fn, this_arg, args, 0, argc + argc_bound);
};
}
@@ -1497,11 +1510,16 @@
// is called and make them non-enumerable and non-configurable.
// To be consistent with our normal functions we leave this as it is.
- // Set the correct length.
- var length = (this.length - argc_bound) > 0 ? this.length - argc_bound : 0;
%FunctionRemovePrototype(result);
%FunctionSetBound(result);
- %BoundFunctionSetLength(result, length);
+ // Set the correct length. If this is a function proxy, this.length might
+ // throw, or return a bogus result. Leave length alone in that case.
+ // TODO(rossberg): This is underspecified in the current proxy proposal.
+ try {
+ var old_length = ToInteger(this.length);
+ var length = (old_length - argc_bound) > 0 ? old_length - argc_bound : 0;
+ %BoundFunctionSetLength(result, length);
+ } catch(x) {}
return result;
}
diff --git a/src/variables.cc b/src/variables.cc
index 6cf6e0b..971061b 100644
--- a/src/variables.cc
+++ b/src/variables.cc
@@ -66,7 +66,7 @@
index_(-1),
local_if_not_shadowed_(NULL),
is_valid_LHS_(is_valid_LHS),
- is_accessed_from_inner_function_scope_(false),
+ is_accessed_from_inner_scope_(false),
is_used_(false) {
// names must be canonicalized for fast equality checks
ASSERT(name->IsSymbol());
diff --git a/src/variables.h b/src/variables.h
index a4ead51..56c8dab 100644
--- a/src/variables.h
+++ b/src/variables.h
@@ -120,12 +120,12 @@
Handle<String> name() const { return name_; }
Mode mode() const { return mode_; }
- bool is_accessed_from_inner_function_scope() const {
- return is_accessed_from_inner_function_scope_;
+ bool is_accessed_from_inner_scope() const {
+ return is_accessed_from_inner_scope_;
}
- void MarkAsAccessedFromInnerFunctionScope() {
+ void MarkAsAccessedFromInnerScope() {
ASSERT(mode_ != TEMPORARY);
- is_accessed_from_inner_function_scope_ = true;
+ is_accessed_from_inner_scope_ = true;
}
bool is_used() { return is_used_; }
void set_is_used(bool flag) { is_used_ = flag; }
@@ -188,7 +188,7 @@
bool is_valid_LHS_;
// Usage info.
- bool is_accessed_from_inner_function_scope_; // set by variable resolver
+ bool is_accessed_from_inner_scope_; // set by variable resolver
bool is_used_;
};
diff --git a/src/version.cc b/src/version.cc
index 4d25e02..e16b63e 100644
--- a/src/version.cc
+++ b/src/version.cc
@@ -34,7 +34,7 @@
// cannot be changed without changing the SCons build script.
#define MAJOR_VERSION 3
#define MINOR_VERSION 6
-#define BUILD_NUMBER 3
+#define BUILD_NUMBER 4
#define PATCH_LEVEL 0
// Use 1 for candidates and 0 otherwise.
// (Boolean macro values are not supported by all preprocessors.)
diff --git a/src/weakmap.js b/src/weakmap.js
index 6c15e30..5fb5151 100644
--- a/src/weakmap.js
+++ b/src/weakmap.js
@@ -85,9 +85,6 @@
// Set up the WeakMap constructor function.
%SetCode($WeakMap, WeakMapConstructor);
- // Set up the WeakMap prototype object.
- %FunctionSetPrototype($WeakMap, new $WeakMap());
-
// Set up the constructor property on the WeakMap prototype object.
%SetProperty($WeakMap.prototype, "constructor", $WeakMap, DONT_ENUM);
diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc
index 7c6f7e3..db06909 100644
--- a/src/x64/builtins-x64.cc
+++ b/src/x64/builtins-x64.cc
@@ -655,15 +655,16 @@
// 2. Get the function to call (passed as receiver) from the stack, check
// if it is a function.
- Label non_function;
+ Label slow, non_function;
// The function to call is at position n+1 on the stack.
__ movq(rdi, Operand(rsp, rax, times_pointer_size, 1 * kPointerSize));
__ JumpIfSmi(rdi, &non_function);
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
- __ j(not_equal, &non_function);
+ __ j(not_equal, &slow);
// 3a. Patch the first argument if necessary when calling a function.
Label shift_arguments;
+ __ Set(rdx, 0); // indicate regular JS_FUNCTION
{ Label convert_to_object, use_global_receiver, patch_receiver;
// Change context eagerly in case we need the global receiver.
__ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
@@ -701,6 +702,7 @@
__ push(rbx);
__ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION);
__ movq(rbx, rax);
+ __ Set(rdx, 0); // indicate regular JS_FUNCTION
__ pop(rax);
__ SmiToInteger32(rax, rax);
@@ -725,14 +727,19 @@
__ jmp(&shift_arguments);
}
+ // 3b. Check for function proxy.
+ __ bind(&slow);
+ __ Set(rdx, 1); // indicate function proxy
+ __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE);
+ __ j(equal, &shift_arguments);
+ __ bind(&non_function);
+ __ Set(rdx, 2); // indicate non-function
- // 3b. Patch the first argument when calling a non-function. The
+ // 3c. Patch the first argument when calling a non-function. The
// CALL_NON_FUNCTION builtin expects the non-function callee as
// receiver, so overwrite the first argument which will ultimately
// become the receiver.
- __ bind(&non_function);
__ movq(Operand(rsp, rax, times_pointer_size, 0), rdi);
- __ Set(rdi, 0);
// 4. Shift arguments and return address one slot down on the stack
// (overwriting the original receiver). Adjust argument count to make
@@ -749,13 +756,26 @@
__ decq(rax); // One fewer argument (first argument is new receiver).
}
- // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin.
- { Label function;
- __ testq(rdi, rdi);
- __ j(not_zero, &function);
+ // 5a. Call non-function via tail call to CALL_NON_FUNCTION builtin,
+ // or a function proxy via CALL_FUNCTION_PROXY.
+ { Label function, non_proxy;
+ __ testq(rdx, rdx);
+ __ j(zero, &function);
__ Set(rbx, 0);
- __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
__ SetCallKind(rcx, CALL_AS_METHOD);
+ __ cmpq(rdx, Immediate(1));
+ __ j(not_equal, &non_proxy);
+
+ __ pop(rdx); // return address
+ __ push(rdi); // re-add proxy object as additional argument
+ __ push(rdx);
+ __ incq(rax);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY);
+ __ jmp(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+
+ __ bind(&non_proxy);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
__ Jump(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
RelocInfo::CODE_TARGET);
__ bind(&function);
@@ -797,11 +817,12 @@
static const int kArgumentsOffset = 2 * kPointerSize;
static const int kReceiverOffset = 3 * kPointerSize;
static const int kFunctionOffset = 4 * kPointerSize;
+
__ push(Operand(rbp, kFunctionOffset));
__ push(Operand(rbp, kArgumentsOffset));
__ InvokeBuiltin(Builtins::APPLY_PREPARE, CALL_FUNCTION);
- // Check the stack for overflow. We are not trying need to catch
+ // Check the stack for overflow. We are not trying to catch
// interruptions (e.g. debug break and preemption) here, so the "real stack
// limit" is checked.
Label okay;
@@ -831,16 +852,20 @@
__ push(rax); // limit
__ push(Immediate(0)); // index
- // Change context eagerly to get the right global object if
- // necessary.
- __ movq(rdi, Operand(rbp, kFunctionOffset));
- __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
-
- // Compute the receiver.
- Label call_to_object, use_global_receiver, push_receiver;
+ // Get the receiver.
__ movq(rbx, Operand(rbp, kReceiverOffset));
+ // Check that the function is a JS function (otherwise it must be a proxy).
+ Label push_receiver;
+ __ movq(rdi, Operand(rbp, kFunctionOffset));
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &push_receiver);
+
+ // Change context eagerly to get the right global object if necessary.
+ __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset));
+
// Do not transform the receiver for strict mode functions.
+ Label call_to_object, use_global_receiver;
__ movq(rdx, FieldOperand(rdi, JSFunction::kSharedFunctionInfoOffset));
__ testb(FieldOperand(rdx, SharedFunctionInfo::kStrictModeByteOffset),
Immediate(1 << SharedFunctionInfo::kStrictModeBitWithinByte));
@@ -913,14 +938,30 @@
__ j(not_equal, &loop);
// Invoke the function.
+ Label call_proxy;
ParameterCount actual(rax);
__ SmiToInteger32(rax, rax);
__ movq(rdi, Operand(rbp, kFunctionOffset));
+ __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
+ __ j(not_equal, &call_proxy);
__ InvokeFunction(rdi, actual, CALL_FUNCTION,
NullCallWrapper(), CALL_AS_METHOD);
__ LeaveInternalFrame();
- __ ret(3 * kPointerSize); // remove function, receiver, and arguments
+ __ ret(3 * kPointerSize); // remove this, receiver, and arguments
+
+ // Invoke the function proxy.
+ __ bind(&call_proxy);
+ __ push(rdi); // add function proxy as last argument
+ __ incq(rax);
+ __ Set(rbx, 0);
+ __ SetCallKind(rcx, CALL_AS_METHOD);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY);
+ __ call(masm->isolate()->builtins()->ArgumentsAdaptorTrampoline(),
+ RelocInfo::CODE_TARGET);
+
+ __ LeaveInternalFrame();
+ __ ret(3 * kPointerSize); // remove this, receiver, and arguments
}
diff --git a/src/x64/code-stubs-x64.cc b/src/x64/code-stubs-x64.cc
index 8a5f59b..df4438b 100644
--- a/src/x64/code-stubs-x64.cc
+++ b/src/x64/code-stubs-x64.cc
@@ -3232,7 +3232,7 @@
void CallFunctionStub::Generate(MacroAssembler* masm) {
- Label slow;
+ Label slow, non_function;
// The receiver might implicitly be the global object. This is
// indicated by passing the hole as the receiver to the call
@@ -3257,7 +3257,7 @@
__ movq(rdi, Operand(rsp, (argc_ + 2) * kPointerSize));
// Check that the function really is a JavaScript function.
- __ JumpIfSmi(rdi, &slow);
+ __ JumpIfSmi(rdi, &non_function);
// Goto slow case if we do not have a function.
__ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx);
__ j(not_equal, &slow);
@@ -3284,15 +3284,32 @@
// Slow-case: Non-function called.
__ bind(&slow);
+ // Check for function proxy.
+ __ CmpInstanceType(rcx, JS_FUNCTION_PROXY_TYPE);
+ __ j(not_equal, &non_function);
+ __ pop(rcx);
+ __ push(rdi); // put proxy as additional argument under return address
+ __ push(rcx);
+ __ Set(rax, argc_ + 1);
+ __ Set(rbx, 0);
+ __ SetCallKind(rcx, CALL_AS_FUNCTION);
+ __ GetBuiltinEntry(rdx, Builtins::CALL_FUNCTION_PROXY);
+ {
+ Handle<Code> adaptor =
+ masm->isolate()->builtins()->ArgumentsAdaptorTrampoline();
+ __ jmp(adaptor, RelocInfo::CODE_TARGET);
+ }
+
// CALL_NON_FUNCTION expects the non-function callee as receiver (instead
// of the original receiver from the call site).
+ __ bind(&non_function);
__ movq(Operand(rsp, (argc_ + 1) * kPointerSize), rdi);
__ Set(rax, argc_);
__ Set(rbx, 0);
+ __ SetCallKind(rcx, CALL_AS_METHOD);
__ GetBuiltinEntry(rdx, Builtins::CALL_NON_FUNCTION);
Handle<Code> adaptor =
Isolate::Current()->builtins()->ArgumentsAdaptorTrampoline();
- __ SetCallKind(rcx, CALL_AS_METHOD);
__ Jump(adaptor, RelocInfo::CODE_TARGET);
}
diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc
index ba8e0f6..bd3e769 100644
--- a/src/x64/full-codegen-x64.cc
+++ b/src/x64/full-codegen-x64.cc
@@ -1926,9 +1926,8 @@
// Record source position for debugger.
SetSourcePosition(expr->position());
// Call the IC initialization code.
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic =
- ISOLATE->stub_cache()->ComputeCallInitialize(arg_count, in_loop, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
__ call(ic, mode, expr->id());
RecordJSReturnSite(expr);
// Restore context register.
@@ -1959,9 +1958,8 @@
// Record source position for debugger.
SetSourcePosition(expr->position());
// Call the IC initialization code.
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
Handle<Code> ic =
- ISOLATE->stub_cache()->ComputeKeyedCallInitialize(arg_count, in_loop);
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arg_count);
__ movq(rcx, Operand(rsp, (arg_count + 1) * kPointerSize)); // Key.
__ call(ic, RelocInfo::CODE_TARGET, expr->id());
RecordJSReturnSite(expr);
@@ -1982,8 +1980,7 @@
}
// Record source position for debugger.
SetSourcePosition(expr->position());
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, flags);
+ CallFunctionStub stub(arg_count, flags);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -2075,8 +2072,7 @@
}
// Record source position for debugger.
SetSourcePosition(expr->position());
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
- CallFunctionStub stub(arg_count, in_loop, RECEIVER_MIGHT_BE_IMPLICIT);
+ CallFunctionStub stub(arg_count, RECEIVER_MIGHT_BE_IMPLICIT);
__ CallStub(&stub);
RecordJSReturnSite(expr);
// Restore context register.
@@ -3477,10 +3473,9 @@
if (expr->is_jsruntime()) {
// Call the JS runtime function using a call IC.
__ Move(rcx, expr->name());
- InLoopFlag in_loop = (loop_depth() > 0) ? IN_LOOP : NOT_IN_LOOP;
RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
Handle<Code> ic =
- ISOLATE->stub_cache()->ComputeCallInitialize(arg_count, in_loop, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arg_count, mode);
__ call(ic, mode, expr->id());
// Restore context register.
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc
index 4146968..9d55594 100644
--- a/src/x64/ic-x64.cc
+++ b/src/x64/ic-x64.cc
@@ -720,7 +720,6 @@
// Probe the stub cache.
Code::Flags flags = Code::ComputeFlags(kind,
- NOT_IN_LOOP,
MONOMORPHIC,
extra_ic_state,
NORMAL,
@@ -1267,9 +1266,7 @@
// -----------------------------------
// Probe the stub cache.
- Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC,
- NOT_IN_LOOP,
- MONOMORPHIC);
+ Code::Flags flags = Code::ComputeFlags(Code::LOAD_IC, MONOMORPHIC);
Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, rax, rcx, rbx,
rdx);
@@ -1372,10 +1369,8 @@
// -----------------------------------
// Get the receiver from the stack and probe the stub cache.
- Code::Flags flags = Code::ComputeFlags(Code::STORE_IC,
- NOT_IN_LOOP,
- MONOMORPHIC,
- strict_mode);
+ Code::Flags flags =
+ Code::ComputeFlags(Code::STORE_IC, MONOMORPHIC, strict_mode);
Isolate::Current()->stub_cache()->GenerateProbe(masm, flags, rdx, rcx, rbx,
no_reg);
diff --git a/src/x64/lithium-codegen-x64.cc b/src/x64/lithium-codegen-x64.cc
index 788ed01..9064a26 100644
--- a/src/x64/lithium-codegen-x64.cc
+++ b/src/x64/lithium-codegen-x64.cc
@@ -2953,8 +2953,8 @@
ASSERT(ToRegister(instr->result()).is(rax));
int arity = instr->arity();
- Handle<Code> ic = isolate()->stub_cache()->ComputeKeyedCallInitialize(
- arity, NOT_IN_LOOP);
+ Handle<Code> ic =
+ isolate()->stub_cache()->ComputeKeyedCallInitialize(arity);
CallCode(ic, RelocInfo::CODE_TARGET, instr);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
}
@@ -2966,7 +2966,7 @@
int arity = instr->arity();
RelocInfo::Mode mode = RelocInfo::CODE_TARGET;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
__ Move(rcx, instr->name());
CallCode(ic, mode, instr);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
@@ -2977,7 +2977,7 @@
ASSERT(ToRegister(instr->result()).is(rax));
int arity = instr->arity();
- CallFunctionStub stub(arity, NOT_IN_LOOP, RECEIVER_MIGHT_BE_IMPLICIT);
+ CallFunctionStub stub(arity, RECEIVER_MIGHT_BE_IMPLICIT);
CallCode(stub.GetCode(), RelocInfo::CODE_TARGET, instr);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));
__ Drop(1);
@@ -2989,7 +2989,7 @@
int arity = instr->arity();
RelocInfo::Mode mode = RelocInfo::CODE_TARGET_CONTEXT;
Handle<Code> ic =
- isolate()->stub_cache()->ComputeCallInitialize(arity, NOT_IN_LOOP, mode);
+ isolate()->stub_cache()->ComputeCallInitialize(arity, mode);
__ Move(rcx, instr->name());
CallCode(ic, mode, instr);
__ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset));