diff options
author | Ben Murdoch <benm@google.com> | 2011-11-30 16:04:58 +0000 |
---|---|---|
committer | Ben Murdoch <benm@google.com> | 2011-12-02 17:28:30 +0000 |
commit | 589d6979ff2ef66fca2d8fa51404c369ca5e9250 (patch) | |
tree | 1d9032fcae9d18a05430a4ba9c14e5c635c4096e /src | |
parent | 69a99ed0b2b2ef69d393c371b03db3a98aaf880e (diff) | |
download | v8-589d6979ff2ef66fca2d8fa51404c369ca5e9250.tar.gz |
Upgrade to V8 3.6
Merge V8 at 3.6.6.11
Simple merge required updates to makefiles only.
Bug: 5688872
Change-Id: Ib38b7ffbcd409585f6cb6fccc59c767029cecc77
Diffstat (limited to 'src')
212 files changed, 9035 insertions, 7252 deletions
diff --git a/src/SConscript b/src/SConscript index 453a7c6a..52607f15 100644 --- a/src/SConscript +++ b/src/SConscript @@ -111,8 +111,8 @@ SOURCES = { runtime.cc runtime-profiler.cc safepoint-table.cc - scanner-base.cc scanner.cc + scanner-character-streams.cc scopeinfo.cc scopes.cc serialize.cc @@ -222,7 +222,7 @@ SOURCES = { 'os:solaris': ['platform-solaris.cc', 'platform-posix.cc'], 'os:cygwin': ['platform-cygwin.cc', 'platform-posix.cc'], 'os:nullos': ['platform-nullos.cc'], - 'os:win32': ['platform-win32.cc'], + 'os:win32': ['platform-win32.cc', 'win32-math.cc'], 'mode:release': [], 'mode:debug': [ 'objects-debug.cc', 'prettyprinter.cc', 'regexp-macro-assembler-tracer.cc' @@ -233,15 +233,25 @@ SOURCES = { PREPARSER_SOURCES = { 'all': Split(""" allocation.cc + bignum.cc + bignum-dtoa.cc + cached-powers.cc + conversions.cc + diy-fp.cc + dtoa.cc + fast-dtoa.cc + fixed-dtoa.cc hashmap.cc preparse-data.cc preparser.cc preparser-api.cc - scanner-base.cc + scanner.cc + strtod.cc token.cc unicode.cc utils.cc - """) + """), + 'os:win32': ['win32-math.cc'] } diff --git a/src/accessors.cc b/src/accessors.cc index e7d6aa0e..951209d9 100644 --- a/src/accessors.cc +++ b/src/accessors.cc @@ -599,6 +599,7 @@ MaybeObject* Accessors::FunctionGetArguments(Object* object, void*) { if (!found_it) return isolate->heap()->undefined_value(); Handle<JSFunction> function(holder, isolate); + if (function->shared()->native()) return isolate->heap()->null_value(); // Find the top invocation of the function by traversing frames. List<JSFunction*> functions(2); for (JavaScriptFrameIterator it(isolate); !it.done(); it.Advance()) { @@ -709,6 +710,7 @@ class FrameFunctionIterator { } while (next_function != NULL); return false; } + private: void GetFunctions() { functions_.Rewind(0); @@ -732,6 +734,7 @@ MaybeObject* Accessors::FunctionGetCaller(Object* object, void*) { bool found_it = false; JSFunction* holder = FindInPrototypeChain<JSFunction>(object, &found_it); if (!found_it) return isolate->heap()->undefined_value(); + if (holder->shared()->native()) return isolate->heap()->null_value(); Handle<JSFunction> function(holder, isolate); FrameFunctionIterator it(isolate, no_alloc); @@ -44,6 +44,7 @@ #include "platform.h" #include "profile-generator-inl.h" #include "runtime-profiler.h" +#include "scanner-character-streams.h" #include "serialize.h" #include "snapshot.h" #include "v8threads.h" @@ -3265,6 +3266,42 @@ bool v8::Object::DeleteHiddenValue(v8::Handle<v8::String> key) { namespace { +static i::ElementsKind GetElementsKindFromExternalArrayType( + ExternalArrayType array_type) { + switch (array_type) { + case kExternalByteArray: + return i::EXTERNAL_BYTE_ELEMENTS; + break; + case kExternalUnsignedByteArray: + return i::EXTERNAL_UNSIGNED_BYTE_ELEMENTS; + break; + case kExternalShortArray: + return i::EXTERNAL_SHORT_ELEMENTS; + break; + case kExternalUnsignedShortArray: + return i::EXTERNAL_UNSIGNED_SHORT_ELEMENTS; + break; + case kExternalIntArray: + return i::EXTERNAL_INT_ELEMENTS; + break; + case kExternalUnsignedIntArray: + return i::EXTERNAL_UNSIGNED_INT_ELEMENTS; + break; + case kExternalFloatArray: + return i::EXTERNAL_FLOAT_ELEMENTS; + break; + case kExternalDoubleArray: + return i::EXTERNAL_DOUBLE_ELEMENTS; + break; + case kExternalPixelArray: + return i::EXTERNAL_PIXEL_ELEMENTS; + break; + } + UNREACHABLE(); + return i::DICTIONARY_ELEMENTS; +} + + void PrepareExternalArrayElements(i::Handle<i::JSObject> object, void* data, ExternalArrayType array_type, @@ -3283,9 +3320,9 @@ void PrepareExternalArrayElements(i::Handle<i::JSObject> object, elements->map() != isolate->heap()->MapForExternalArrayType(array_type); if (cant_reuse_map) { i::Handle<i::Map> external_array_map = - isolate->factory()->GetExternalArrayElementsMap( + isolate->factory()->GetElementsTransitionMap( i::Handle<i::Map>(object->map()), - array_type, + GetElementsKindFromExternalArrayType(array_type), object->HasFastProperties()); object->set_map(*external_array_map); } @@ -3347,6 +3384,7 @@ int v8::Object::GetIndexedPropertiesPixelDataLength() { } } + void v8::Object::SetIndexedPropertiesToExternalArrayData( void* data, ExternalArrayType array_type, diff --git a/src/arguments.h b/src/arguments.h index 72bbe1dd..e9a32702 100644 --- a/src/arguments.h +++ b/src/arguments.h @@ -75,6 +75,7 @@ class Arguments BASE_EMBEDDED { int length() const { return length_; } Object** arguments() { return arguments_; } + private: int length_; Object** arguments_; diff --git a/src/arm/builtins-arm.cc b/src/arm/builtins-arm.cc index a35380c1..60d2081c 100644 --- a/src/arm/builtins-arm.cc +++ b/src/arm/builtins-arm.cc @@ -1230,16 +1230,17 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 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 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ 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 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ 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 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ 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 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ 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 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ mov(r1, Operand(0, RelocInfo::NONE)); // initial index __ push(r1); + // 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(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)); + __ 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. - Label call_to_object, use_global_receiver, push_receiver; - __ ldr(r0, MemOperand(fp, kRecvOffset)); - // 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 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ 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 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ 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 09d2c170..e65f6d9b 100644 --- a/src/arm/code-stubs-arm.cc +++ b/src/arm/code-stubs-arm.cc @@ -3432,7 +3432,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, // Retrieve the pending exception and clear the variable. __ mov(ip, Operand(ExternalReference::the_hole_value_location(isolate))); __ ldr(r3, MemOperand(ip)); - __ mov(ip, Operand(ExternalReference(Isolate::k_pending_exception_address, + __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress, isolate))); __ ldr(r0, MemOperand(ip)); __ str(r3, MemOperand(ip)); @@ -3567,7 +3567,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ mov(r7, Operand(Smi::FromInt(marker))); __ mov(r6, Operand(Smi::FromInt(marker))); __ mov(r5, - Operand(ExternalReference(Isolate::k_c_entry_fp_address, isolate))); + Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate))); __ ldr(r5, MemOperand(r5)); __ Push(r8, r7, r6, r5); @@ -3576,7 +3576,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // If this is the outermost JS call, set js_entry_sp value. Label non_outermost_js; - ExternalReference js_entry_sp(Isolate::k_js_entry_sp_address, isolate); + ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress, isolate); __ mov(r5, Operand(ExternalReference(js_entry_sp))); __ ldr(r6, MemOperand(r5)); __ cmp(r6, Operand::Zero()); @@ -3597,7 +3597,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // exception field in the JSEnv and return a failure sentinel. // Coming in here the fp will be invalid because the PushTryHandler below // sets it to 0 to signal the existence of the JSEntry frame. - __ mov(ip, Operand(ExternalReference(Isolate::k_pending_exception_address, + __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress, isolate))); __ str(r0, MemOperand(ip)); __ mov(r0, Operand(reinterpret_cast<int32_t>(Failure::Exception()))); @@ -3615,7 +3615,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Clear any pending exceptions. __ mov(ip, Operand(ExternalReference::the_hole_value_location(isolate))); __ ldr(r5, MemOperand(ip)); - __ mov(ip, Operand(ExternalReference(Isolate::k_pending_exception_address, + __ mov(ip, Operand(ExternalReference(Isolate::kPendingExceptionAddress, isolate))); __ str(r5, MemOperand(ip)); @@ -3662,7 +3662,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Restore the top frame descriptors from the stack. __ pop(r3); __ mov(ip, - Operand(ExternalReference(Isolate::k_c_entry_fp_address, isolate))); + Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate))); __ str(r3, MemOperand(ip)); // Reset the stack to the callee saved registers. @@ -4487,7 +4487,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // frame. Therefore we have to use fp, which points exactly to two pointer // sizes below the previous sp. (Because creating a new stack frame pushes // the previous fp onto the stack and moves up sp by 2 * kPointerSize.) - __ ldr(r0, MemOperand(fp, kSubjectOffset + 2 * kPointerSize)); + __ ldr(subject, MemOperand(fp, kSubjectOffset + 2 * kPointerSize)); // If slice offset is not 0, load the length from the original sliced string. // Argument 4, r3: End of string data // Argument 3, r2: Start of string data @@ -4495,7 +4495,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ add(r9, r8, Operand(r9, LSL, r3)); __ add(r2, r9, Operand(r1, LSL, r3)); - __ ldr(r8, FieldMemOperand(r0, String::kLengthOffset)); + __ ldr(r8, FieldMemOperand(subject, String::kLengthOffset)); __ mov(r8, Operand(r8, ASR, kSmiTagSize)); __ add(r3, r9, Operand(r8, LSL, r3)); @@ -4503,7 +4503,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Already there // Argument 1 (r0): Subject string. - // Already there + __ mov(r0, subject); // Locate the code entry and call it. __ add(r7, r7, Operand(Code::kHeaderSize - kHeapObjectTag)); @@ -4520,12 +4520,12 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Check the result. Label success; - __ cmp(subject, Operand(NativeRegExpMacroAssembler::SUCCESS)); + __ cmp(r0, Operand(NativeRegExpMacroAssembler::SUCCESS)); __ b(eq, &success); Label failure; - __ cmp(subject, Operand(NativeRegExpMacroAssembler::FAILURE)); + __ cmp(r0, Operand(NativeRegExpMacroAssembler::FAILURE)); __ b(eq, &failure); - __ cmp(subject, Operand(NativeRegExpMacroAssembler::EXCEPTION)); + __ cmp(r0, Operand(NativeRegExpMacroAssembler::EXCEPTION)); // If not exception it can only be retry. Handle that in the runtime system. __ b(ne, &runtime); // Result must now be exception. If there is no pending exception already a @@ -4534,21 +4534,21 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // TODO(592): Rerunning the RegExp to get the stack overflow exception. __ mov(r1, Operand(ExternalReference::the_hole_value_location(isolate))); __ ldr(r1, MemOperand(r1, 0)); - __ mov(r2, Operand(ExternalReference(Isolate::k_pending_exception_address, + __ mov(r2, Operand(ExternalReference(Isolate::kPendingExceptionAddress, isolate))); __ ldr(r0, MemOperand(r2, 0)); - __ cmp(subject, r1); + __ cmp(r0, r1); __ b(eq, &runtime); __ str(r1, MemOperand(r2, 0)); // Clear pending exception. // Check if the exception is a termination. If so, throw as uncatchable. - __ LoadRoot(ip, Heap::kTerminationExceptionRootIndex); - __ cmp(subject, ip); + __ CompareRoot(r0, Heap::kTerminationExceptionRootIndex); + Label termination_exception; __ b(eq, &termination_exception); - __ Throw(subject); // Expects thrown value in r0. + __ Throw(r0); // Expects thrown value in r0. __ bind(&termination_exception); __ ThrowUncatchable(TERMINATION, r0); // Expects thrown value in r0. @@ -4713,7 +4713,7 @@ void RegExpConstructResultStub::Generate(MacroAssembler* masm) { 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 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // 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 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // 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)); @@ -4894,7 +4909,8 @@ void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { // Check for 1-byte or 2-byte string. __ bind(&flat_string); - STATIC_ASSERT(kAsciiStringTag != 0); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ tst(result_, Operand(kStringEncodingMask)); __ b(ne, &ascii_string); @@ -5468,11 +5484,6 @@ void SubStringStub::Generate(MacroAssembler* masm) { Register to = r6; Register from = r7; - if (FLAG_string_slices) { - __ nop(0); // Jumping as first instruction would crash the code generation. - __ jmp(&runtime); - } - __ Ldrd(to, from, MemOperand(sp, kToOffset)); STATIC_ASSERT(kFromOffset == kToOffset + 4); STATIC_ASSERT(kSmiTag == 0); @@ -5490,64 +5501,79 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ b(mi, &runtime); // Fail if from > to. // Special handling of sub-strings of length 1 and 2. One character strings // are handled in the runtime system (looked up in the single character - // cache). Two character strings are looked for in the symbol cache. + // cache). Two character strings are looked for in the symbol cache in + // generated code. __ cmp(r2, Operand(2)); __ b(lt, &runtime); - // r2: length - // r3: from index (untaged smi) + // r2: result string length + // r3: from index (untagged smi) // r6 (a.k.a. to): to (smi) // r7 (a.k.a. from): from offset (smi) - // Make sure first argument is a sequential (or flat) string. - __ ldr(r5, MemOperand(sp, kStringOffset)); + __ ldr(r0, MemOperand(sp, kStringOffset)); STATIC_ASSERT(kSmiTag == 0); - __ JumpIfSmi(r5, &runtime); - Condition is_string = masm->IsObjectStringType(r5, r1); + __ JumpIfSmi(r0, &runtime); + Condition is_string = masm->IsObjectStringType(r0, r1); __ b(NegateCondition(is_string), &runtime); + // Short-cut for the case of trivial substring. + Label return_r0; + // r0: original string + // r2: result string length + __ ldr(r4, FieldMemOperand(r0, String::kLengthOffset)); + __ cmp(r2, Operand(r4, ASR, 1)); + __ b(eq, &return_r0); + + Label create_slice; + if (FLAG_string_slices) { + __ cmp(r2, Operand(SlicedString::kMinLength)); + __ b(ge, &create_slice); + } + + // r0: original string // r1: instance type - // r2: length + // r2: result string length // r3: from index (untagged smi) - // r5: string // r6 (a.k.a. to): to (smi) // r7 (a.k.a. from): from offset (smi) Label seq_string; __ and_(r4, r1, Operand(kStringRepresentationMask)); STATIC_ASSERT(kSeqStringTag < kConsStringTag); STATIC_ASSERT(kConsStringTag < kExternalStringTag); + STATIC_ASSERT(kConsStringTag < kSlicedStringTag); __ cmp(r4, Operand(kConsStringTag)); - __ b(gt, &runtime); // External strings go to runtime. + __ b(gt, &runtime); // Slices and external strings go to runtime. __ b(lt, &seq_string); // Sequential strings are handled directly. // Cons string. Try to recurse (once) on the first substring. // (This adds a little more generality than necessary to handle flattened // cons strings, but not much). - __ ldr(r5, FieldMemOperand(r5, ConsString::kFirstOffset)); - __ ldr(r4, FieldMemOperand(r5, HeapObject::kMapOffset)); + __ ldr(r0, FieldMemOperand(r0, ConsString::kFirstOffset)); + __ ldr(r4, FieldMemOperand(r0, HeapObject::kMapOffset)); __ ldrb(r1, FieldMemOperand(r4, Map::kInstanceTypeOffset)); __ tst(r1, Operand(kStringRepresentationMask)); STATIC_ASSERT(kSeqStringTag == 0); - __ b(ne, &runtime); // Cons and External strings go to runtime. + __ b(ne, &runtime); // Cons, slices and external strings go to runtime. // Definitly a sequential string. __ bind(&seq_string); - // r1: instance type. - // r2: length - // r3: from index (untaged smi) - // r5: string + // r0: original string + // r1: instance type + // r2: result string length + // r3: from index (untagged smi) // r6 (a.k.a. to): to (smi) // r7 (a.k.a. from): from offset (smi) - __ ldr(r4, FieldMemOperand(r5, String::kLengthOffset)); + __ ldr(r4, FieldMemOperand(r0, String::kLengthOffset)); __ cmp(r4, Operand(to)); __ b(lt, &runtime); // Fail if to > length. to = no_reg; - // r1: instance type. - // r2: result string length. - // r3: from index (untaged smi) - // r5: string. + // r0: original string or left hand side of the original cons string. + // r1: instance type + // r2: result string length + // r3: from index (untagged smi) // r7 (a.k.a. from): from offset (smi) // Check for flat ASCII string. Label non_ascii_flat; @@ -5561,82 +5587,146 @@ void SubStringStub::Generate(MacroAssembler* masm) { // Sub string of length 2 requested. // Get the two characters forming the sub string. - __ add(r5, r5, Operand(r3)); - __ ldrb(r3, FieldMemOperand(r5, SeqAsciiString::kHeaderSize)); - __ ldrb(r4, FieldMemOperand(r5, SeqAsciiString::kHeaderSize + 1)); + __ add(r0, r0, Operand(r3)); + __ ldrb(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize)); + __ ldrb(r4, FieldMemOperand(r0, SeqAsciiString::kHeaderSize + 1)); // Try to lookup two character string in symbol table. Label make_two_character_string; StringHelper::GenerateTwoCharacterSymbolTableProbe( masm, r3, r4, r1, r5, r6, r7, r9, &make_two_character_string); Counters* counters = masm->isolate()->counters(); - __ IncrementCounter(counters->sub_string_native(), 1, r3, r4); - __ add(sp, sp, Operand(3 * kPointerSize)); - __ Ret(); + __ jmp(&return_r0); // r2: result string length. // r3: two characters combined into halfword in little endian byte order. __ bind(&make_two_character_string); __ AllocateAsciiString(r0, r2, r4, r5, r9, &runtime); __ strh(r3, FieldMemOperand(r0, SeqAsciiString::kHeaderSize)); - __ IncrementCounter(counters->sub_string_native(), 1, r3, r4); - __ add(sp, sp, Operand(3 * kPointerSize)); - __ Ret(); + __ jmp(&return_r0); __ bind(&result_longer_than_two); + // Locate 'from' character of string. + __ add(r5, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + __ add(r5, r5, Operand(from, ASR, 1)); + // Allocate the result. __ AllocateAsciiString(r0, r2, r3, r4, r1, &runtime); - // r0: result string. - // r2: result string length. - // r5: string. + // r0: result string + // r2: result string length + // r5: first character of substring to copy // r7 (a.k.a. from): from offset (smi) // Locate first character of result. __ add(r1, r0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // Locate 'from' character of string. - __ add(r5, r5, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - __ add(r5, r5, Operand(from, ASR, 1)); - // r0: result string. - // r1: first character of result string. - // r2: result string length. - // r5: first character of sub string to copy. + // r0: result string + // r1: first character of result string + // r2: result string length + // r5: first character of substring to copy STATIC_ASSERT((SeqAsciiString::kHeaderSize & kObjectAlignmentMask) == 0); StringHelper::GenerateCopyCharactersLong(masm, r1, r5, r2, r3, r4, r6, r7, r9, COPY_ASCII | DEST_ALWAYS_ALIGNED); - __ IncrementCounter(counters->sub_string_native(), 1, r3, r4); - __ add(sp, sp, Operand(3 * kPointerSize)); - __ Ret(); + __ jmp(&return_r0); __ bind(&non_ascii_flat); - // r2: result string length. - // r5: string. + // r0: original string + // r2: result string length // r7 (a.k.a. from): from offset (smi) // Check for flat two byte string. + // Locate 'from' character of string. + __ add(r5, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + // As "from" is a smi it is 2 times the value which matches the size of a two + // byte character. + STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); + __ add(r5, r5, Operand(from)); + // Allocate the result. __ AllocateTwoByteString(r0, r2, r1, r3, r4, &runtime); - // r0: result string. - // r2: result string length. - // r5: string. + // r0: result string + // r2: result string length + // r5: first character of substring to copy // Locate first character of result. __ add(r1, r0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // Locate 'from' character of string. - __ add(r5, r5, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // As "from" is a smi it is 2 times the value which matches the size of a two - // byte character. - __ add(r5, r5, Operand(from)); + from = no_reg; // r0: result string. // r1: first character of result. // r2: result length. - // r5: first character of string to copy. + // r5: first character of substring to copy. STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0); StringHelper::GenerateCopyCharactersLong( masm, r1, r5, r2, r3, r4, r6, r7, r9, DEST_ALWAYS_ALIGNED); + __ jmp(&return_r0); + + if (FLAG_string_slices) { + __ bind(&create_slice); + // r0: original string + // r1: instance type + // r2: length + // r3: from index (untagged smi) + // r6 (a.k.a. to): to (smi) + // r7 (a.k.a. from): from offset (smi) + Label allocate_slice, sliced_string, seq_string; + STATIC_ASSERT(kSeqStringTag == 0); + __ tst(r1, Operand(kStringRepresentationMask)); + __ b(eq, &seq_string); + STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); + STATIC_ASSERT(kIsIndirectStringMask != 0); + __ tst(r1, Operand(kIsIndirectStringMask)); + // External string. Jump to runtime. + __ b(eq, &runtime); + + __ tst(r1, Operand(kSlicedNotConsMask)); + __ b(ne, &sliced_string); + // Cons string. Check whether it is flat, then fetch first part. + __ ldr(r5, FieldMemOperand(r0, ConsString::kSecondOffset)); + __ LoadRoot(r9, Heap::kEmptyStringRootIndex); + __ cmp(r5, r9); + __ b(ne, &runtime); + __ ldr(r5, FieldMemOperand(r0, ConsString::kFirstOffset)); + __ jmp(&allocate_slice); + + __ bind(&sliced_string); + // Sliced string. Fetch parent and correct start index by offset. + __ ldr(r5, FieldMemOperand(r0, SlicedString::kOffsetOffset)); + __ add(r7, r7, r5); + __ ldr(r5, FieldMemOperand(r0, SlicedString::kParentOffset)); + __ jmp(&allocate_slice); + + __ bind(&seq_string); + // Sequential string. Just move string to the right register. + __ mov(r5, r0); + + __ bind(&allocate_slice); + // r1: instance type of original string + // r2: length + // r5: underlying subject string + // r7 (a.k.a. from): from offset (smi) + // Allocate new sliced string. At this point we do not reload the instance + // type including the string encoding because we simply rely on the info + // provided by the original string. It does not matter if the original + // string's encoding is wrong because we always have to recheck encoding of + // the newly created string's parent anyways due to externalized strings. + Label two_byte_slice, set_slice_header; + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ tst(r1, Operand(kStringEncodingMask)); + __ b(eq, &two_byte_slice); + __ AllocateAsciiSlicedString(r0, r2, r3, r4, &runtime); + __ jmp(&set_slice_header); + __ bind(&two_byte_slice); + __ AllocateTwoByteSlicedString(r0, r2, r3, r4, &runtime); + __ bind(&set_slice_header); + __ str(r7, FieldMemOperand(r0, SlicedString::kOffsetOffset)); + __ str(r5, FieldMemOperand(r0, SlicedString::kParentOffset)); + } + + __ bind(&return_r0); __ IncrementCounter(counters->sub_string_native(), 1, r3, r4); __ add(sp, sp, Operand(3 * kPointerSize)); __ Ret(); diff --git a/src/arm/full-codegen-arm.cc b/src/arm/full-codegen-arm.cc index 1604883b..50ed8b1d 100644 --- a/src/arm/full-codegen-arm.cc +++ b/src/arm/full-codegen-arm.cc @@ -193,14 +193,14 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // Copy any necessary parameters into the context. int num_parameters = info->scope()->num_parameters(); for (int i = 0; i < num_parameters; i++) { - Slot* slot = scope()->parameter(i)->AsSlot(); - if (slot != NULL && slot->type() == Slot::CONTEXT) { + Variable* var = scope()->parameter(i); + if (var->IsContextSlot()) { int parameter_offset = StandardFrameConstants::kCallerSPOffset + (num_parameters - 1 - i) * kPointerSize; // Load parameter from stack. __ ldr(r0, MemOperand(fp, parameter_offset)); // Store it in the context. - __ mov(r1, Operand(Context::SlotOffset(slot->index()))); + __ mov(r1, Operand(Context::SlotOffset(var->index()))); __ str(r0, MemOperand(cp, r1)); // Update the write barrier. This clobbers all involved // registers, so we have to use two more registers to avoid @@ -244,7 +244,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { ArgumentsAccessStub stub(type); __ CallStub(&stub); - Move(arguments->AsSlot(), r0, r1, r2); + SetVar(arguments, r0, r1, r2); } if (FLAG_trace) { @@ -258,17 +258,19 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { scope()->VisitIllegalRedeclaration(this); } else { + PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS); { Comment cmnt(masm_, "[ Declarations"); // For named function expressions, declare the function name as a // constant. if (scope()->is_function_scope() && scope()->function() != NULL) { - EmitDeclaration(scope()->function(), Variable::CONST, NULL); + int ignored = 0; + EmitDeclaration(scope()->function(), Variable::CONST, NULL, &ignored); } VisitDeclarations(scope()->declarations()); } { Comment cmnt(masm_, "[ Stack check"); - PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS); + PrepareForBailoutForId(AstNode::kDeclarationsId, NO_REGISTERS); Label ok; __ LoadRoot(ip, Heap::kStackLimitRootIndex); __ cmp(sp, Operand(ip)); @@ -367,24 +369,28 @@ void FullCodeGenerator::EmitReturnSequence() { } -void FullCodeGenerator::EffectContext::Plug(Slot* slot) const { +void FullCodeGenerator::EffectContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); } -void FullCodeGenerator::AccumulatorValueContext::Plug(Slot* slot) const { - codegen()->Move(result_register(), slot); +void FullCodeGenerator::AccumulatorValueContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + codegen()->GetVar(result_register(), var); } -void FullCodeGenerator::StackValueContext::Plug(Slot* slot) const { - codegen()->Move(result_register(), slot); +void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + codegen()->GetVar(result_register(), var); __ push(result_register()); } -void FullCodeGenerator::TestContext::Plug(Slot* slot) const { +void FullCodeGenerator::TestContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); // For simplicity we always test the accumulator register. - codegen()->Move(result_register(), slot); + codegen()->GetVar(result_register(), var); codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); codegen()->DoTest(this); } @@ -616,45 +622,54 @@ void FullCodeGenerator::Split(Condition cond, } -MemOperand FullCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) { - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: - return MemOperand(fp, SlotOffset(slot)); - case Slot::CONTEXT: { - int context_chain_length = - scope()->ContextChainLength(slot->var()->scope()); - __ LoadContext(scratch, context_chain_length); - return ContextOperand(scratch, slot->index()); - } - case Slot::LOOKUP: - UNREACHABLE(); +MemOperand FullCodeGenerator::StackOperand(Variable* var) { + ASSERT(var->IsStackAllocated()); + // Offset is negative because higher indexes are at lower addresses. + int offset = -var->index() * kPointerSize; + // Adjust by a (parameter or local) base offset. + if (var->IsParameter()) { + offset += (info_->scope()->num_parameters() + 1) * kPointerSize; + } else { + offset += JavaScriptFrameConstants::kLocal0Offset; } - UNREACHABLE(); - return MemOperand(r0, 0); + return MemOperand(fp, offset); } -void FullCodeGenerator::Move(Register destination, Slot* source) { +MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) { + ASSERT(var->IsContextSlot() || var->IsStackAllocated()); + if (var->IsContextSlot()) { + int context_chain_length = scope()->ContextChainLength(var->scope()); + __ LoadContext(scratch, context_chain_length); + return ContextOperand(scratch, var->index()); + } else { + return StackOperand(var); + } +} + + +void FullCodeGenerator::GetVar(Register dest, Variable* var) { // Use destination as scratch. - MemOperand slot_operand = EmitSlotSearch(source, destination); - __ ldr(destination, slot_operand); + MemOperand location = VarOperand(var, dest); + __ ldr(dest, location); } -void FullCodeGenerator::Move(Slot* dst, - Register src, - Register scratch1, - Register scratch2) { - ASSERT(dst->type() != Slot::LOOKUP); // Not yet implemented. - ASSERT(!scratch1.is(src) && !scratch2.is(src)); - MemOperand location = EmitSlotSearch(dst, scratch1); +void FullCodeGenerator::SetVar(Variable* var, + Register src, + Register scratch0, + Register scratch1) { + ASSERT(var->IsContextSlot() || var->IsStackAllocated()); + ASSERT(!scratch0.is(src)); + ASSERT(!scratch0.is(scratch1)); + ASSERT(!scratch1.is(src)); + MemOperand location = VarOperand(var, scratch0); __ str(src, location); // Emit the write barrier code if the location is in the heap. - if (dst->type() == Slot::CONTEXT) { - __ RecordWrite(scratch1, - Operand(Context::SlotOffset(dst->index())), - scratch2, + if (var->IsContextSlot()) { + __ RecordWrite(scratch0, + Operand(Context::SlotOffset(var->index())), + scratch1, src); } } @@ -687,29 +702,33 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, } -void FullCodeGenerator::EmitDeclaration(Variable* variable, +void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Variable::Mode mode, - FunctionLiteral* function) { - Comment cmnt(masm_, "[ Declaration"); - ASSERT(variable != NULL); // Must have been resolved. - Slot* slot = variable->AsSlot(); - ASSERT(slot != NULL); - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: + FunctionLiteral* function, + int* global_count) { + // If it was not possible to allocate the variable at compile time, we + // need to "declare" it at runtime to make sure it actually exists in the + // local context. + Variable* variable = proxy->var(); + switch (variable->location()) { + case Variable::UNALLOCATED: + ++(*global_count); + break; + + case Variable::PARAMETER: + case Variable::LOCAL: if (function != NULL) { + Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); - __ str(result_register(), MemOperand(fp, SlotOffset(slot))); + __ str(result_register(), StackOperand(variable)); } else if (mode == Variable::CONST || mode == Variable::LET) { + Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ str(ip, MemOperand(fp, SlotOffset(slot))); + __ str(ip, StackOperand(variable)); } break; - case Slot::CONTEXT: - // We bypass the general EmitSlotSearch because we know more about - // this specific context. - + case Variable::CONTEXT: // The variable in the decl always resides in the current function // context. ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); @@ -722,22 +741,27 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, __ Check(ne, "Declaration in catch context."); } if (function != NULL) { + Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); - __ str(result_register(), ContextOperand(cp, slot->index())); - int offset = Context::SlotOffset(slot->index()); + __ str(result_register(), ContextOperand(cp, variable->index())); + int offset = Context::SlotOffset(variable->index()); // We know that we have written a function, which is not a smi. __ mov(r1, Operand(cp)); __ RecordWrite(r1, Operand(offset), r2, result_register()); + PrepareForBailoutForId(proxy->id(), NO_REGISTERS); } else if (mode == Variable::CONST || mode == Variable::LET) { + Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ str(ip, ContextOperand(cp, slot->index())); + __ str(ip, ContextOperand(cp, variable->index())); // No write barrier since the_hole_value is in old space. + PrepareForBailoutForId(proxy->id(), NO_REGISTERS); } break; - case Slot::LOOKUP: { + case Variable::LOOKUP: { + Comment cmnt(masm_, "[ Declaration"); __ mov(r2, Operand(variable->name())); - // Declaration nodes are always introduced in one of two modes. + // Declaration nodes are always introduced in one of three modes. ASSERT(mode == Variable::VAR || mode == Variable::CONST || mode == Variable::LET); @@ -755,7 +779,7 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, __ LoadRoot(r0, Heap::kTheHoleValueRootIndex); __ Push(cp, r2, r1, r0); } else { - __ mov(r0, Operand(Smi::FromInt(0))); // No initial value! + __ mov(r0, Operand(Smi::FromInt(0))); // Indicates no initial value. __ Push(cp, r2, r1, r0); } __ CallRuntime(Runtime::kDeclareContextSlot, 4); @@ -765,19 +789,16 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, } -void FullCodeGenerator::VisitDeclaration(Declaration* decl) { - EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); -} +void FullCodeGenerator::VisitDeclaration(Declaration* decl) { } void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { // Call the runtime to declare the globals. // The context is the first argument. - __ mov(r2, Operand(pairs)); - __ mov(r1, Operand(Smi::FromInt(is_eval() ? 1 : 0))); - __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); - __ Push(cp, r2, r1, r0); - __ CallRuntime(Runtime::kDeclareGlobals, 4); + __ mov(r1, Operand(pairs)); + __ mov(r0, Operand(Smi::FromInt(DeclareGlobalsFlags()))); + __ Push(cp, r1, r0); + __ CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. } @@ -1085,10 +1106,9 @@ void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { } -void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( - Slot* slot, - TypeofState typeof_state, - Label* slow) { +void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, + TypeofState typeof_state, + Label* slow) { Register current = cp; Register next = r1; Register temp = r2; @@ -1135,7 +1155,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( } __ ldr(r0, GlobalObjectOperand()); - __ mov(r2, Operand(slot->var()->name())); + __ mov(r2, Operand(var->name())); RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) ? RelocInfo::CODE_TARGET : RelocInfo::CODE_TARGET_CONTEXT; @@ -1144,15 +1164,14 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( } -MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( - Slot* slot, - Label* slow) { - ASSERT(slot->type() == Slot::CONTEXT); +MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, + Label* slow) { + ASSERT(var->IsContextSlot()); Register context = cp; Register next = r3; Register temp = r4; - for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { + for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { if (s->num_heap_slots() > 0) { if (s->calls_eval()) { // Check that extension is NULL. @@ -1173,59 +1192,30 @@ MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( // This function is used only for loads, not stores, so it's safe to // return an cp-based operand (the write barrier cannot be allowed to // destroy the cp register). - return ContextOperand(context, slot->index()); + return ContextOperand(context, var->index()); } -void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( - Slot* slot, - TypeofState typeof_state, - Label* slow, - Label* done) { +void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var, + TypeofState typeof_state, + Label* slow, + Label* done) { // Generate fast-case code for variables that might be shadowed by // eval-introduced variables. Eval is used a lot without // introducing variables. In those cases, we do not want to // perform a runtime call for all variables in the scope // containing the eval. - if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) { - EmitLoadGlobalSlotCheckExtensions(slot, typeof_state, slow); + if (var->mode() == Variable::DYNAMIC_GLOBAL) { + EmitLoadGlobalCheckExtensions(var, typeof_state, slow); __ jmp(done); - } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { - Slot* potential_slot = slot->var()->local_if_not_shadowed()->AsSlot(); - Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite(); - if (potential_slot != NULL) { - // Generate fast case for locals that rewrite to slots. - __ ldr(r0, ContextSlotOperandCheckExtensions(potential_slot, slow)); - if (potential_slot->var()->mode() == Variable::CONST) { - __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(r0, ip); - __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq); - } - __ jmp(done); - } else if (rewrite != NULL) { - // Generate fast case for calls of an argument function. - Property* property = rewrite->AsProperty(); - if (property != NULL) { - VariableProxy* obj_proxy = property->obj()->AsVariableProxy(); - Literal* key_literal = property->key()->AsLiteral(); - if (obj_proxy != NULL && - key_literal != NULL && - obj_proxy->IsArguments() && - key_literal->handle()->IsSmi()) { - // Load arguments object if there are no eval-introduced - // variables. Then load the argument from the arguments - // object using keyed load. - __ ldr(r1, - ContextSlotOperandCheckExtensions(obj_proxy->var()->AsSlot(), - slow)); - __ mov(r0, Operand(key_literal->handle())); - Handle<Code> ic = - isolate()->builtins()->KeyedLoadIC_Initialize(); - __ Call(ic, RelocInfo::CODE_TARGET, GetPropertyId(property)); - __ jmp(done); - } - } + } else if (var->mode() == Variable::DYNAMIC_LOCAL) { + Variable* local = var->local_if_not_shadowed(); + __ ldr(r0, ContextSlotOperandCheckExtensions(local, slow)); + if (local->mode() == Variable::CONST) { + __ CompareRoot(r0, Heap::kTheHoleValueRootIndex); + __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq); } + __ jmp(done); } } @@ -1235,66 +1225,60 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { SetSourcePosition(proxy->position()); Variable* var = proxy->var(); - // Three cases: non-this global variables, lookup slots, and all other - // types of slots. - Slot* slot = var->AsSlot(); - ASSERT((var->is_global() && !var->is_this()) == (slot == NULL)); - - if (slot == NULL) { - Comment cmnt(masm_, "Global variable"); - // Use inline caching. Variable name is passed in r2 and the global - // object (receiver) in r0. - __ ldr(r0, GlobalObjectOperand()); - __ mov(r2, Operand(var->name())); - Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); - __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); - context()->Plug(r0); - - } else if (slot->type() == Slot::LOOKUP) { - Label done, slow; - - // Generate code for loading from variables potentially shadowed - // by eval-introduced variables. - EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done); - - __ bind(&slow); - Comment cmnt(masm_, "Lookup slot"); - __ mov(r1, Operand(var->name())); - __ Push(cp, r1); // Context and name. - __ CallRuntime(Runtime::kLoadContextSlot, 2); - __ bind(&done); + // Three cases: global variables, lookup variables, and all other types of + // variables. + switch (var->location()) { + case Variable::UNALLOCATED: { + Comment cmnt(masm_, "Global variable"); + // Use inline caching. Variable name is passed in r2 and the global + // object (receiver) in r0. + __ ldr(r0, GlobalObjectOperand()); + __ mov(r2, Operand(var->name())); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); + __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); + context()->Plug(r0); + break; + } - context()->Plug(r0); + case Variable::PARAMETER: + case Variable::LOCAL: + case Variable::CONTEXT: { + Comment cmnt(masm_, var->IsContextSlot() + ? "Context variable" + : "Stack variable"); + if (var->mode() != Variable::LET && var->mode() != Variable::CONST) { + context()->Plug(var); + } else { + // Let and const need a read barrier. + GetVar(r0, var); + __ CompareRoot(r0, Heap::kTheHoleValueRootIndex); + if (var->mode() == Variable::LET) { + Label done; + __ b(ne, &done); + __ mov(r0, Operand(var->name())); + __ push(r0); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ bind(&done); + } else { + __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq); + } + context()->Plug(r0); + } + break; + } - } else { - Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) - ? "Context slot" - : "Stack slot"); - if (var->mode() == Variable::CONST) { - // Constants may be the hole value if they have not been initialized. - // Unhole them. - MemOperand slot_operand = EmitSlotSearch(slot, r0); - __ ldr(r0, slot_operand); - __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(r0, ip); - __ LoadRoot(r0, Heap::kUndefinedValueRootIndex, eq); - context()->Plug(r0); - } else if (var->mode() == Variable::LET) { - // Let bindings may be the hole value if they have not been initialized. - // Throw a type error in this case. - Label done; - MemOperand slot_operand = EmitSlotSearch(slot, r0); - __ ldr(r0, slot_operand); - __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(r0, ip); - __ b(ne, &done); - __ mov(r0, Operand(var->name())); - __ push(r0); - __ CallRuntime(Runtime::kThrowReferenceError, 1); + case Variable::LOOKUP: { + Label done, slow; + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done); + __ bind(&slow); + Comment cmnt(masm_, "Lookup variable"); + __ mov(r1, Operand(var->name())); + __ Push(cp, r1); // Context and name. + __ CallRuntime(Runtime::kLoadContextSlot, 2); __ bind(&done); context()->Plug(r0); - } else { - context()->Plug(slot); } } } @@ -1828,14 +1812,8 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) { - ASSERT(var != NULL); - ASSERT(var->is_global() || var->AsSlot() != NULL); - - if (var->is_global()) { - ASSERT(!var->is_this()); - // Assignment to a global variable. Use inline caching for the - // assignment. Right-hand-side value is passed in r0, variable name in - // r2, and the global object in r1. + if (var->IsUnallocated()) { + // Global var, const, or let. __ mov(r2, Operand(var->name())); __ ldr(r1, GlobalObjectOperand()); Handle<Code> ic = is_strict_mode() @@ -1844,120 +1822,83 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); } else if (op == Token::INIT_CONST) { - // Like var declarations, const declarations are hoisted to function - // scope. However, unlike var initializers, const initializers are able - // to drill a hole to that function context, even from inside a 'with' - // context. We thus bypass the normal static scope lookup. - Slot* slot = var->AsSlot(); - Label skip; - switch (slot->type()) { - case Slot::PARAMETER: - // No const parameters. - UNREACHABLE(); - break; - case Slot::LOCAL: - // Detect const reinitialization by checking for the hole value. - __ ldr(r1, MemOperand(fp, SlotOffset(slot))); - __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(r1, ip); - __ b(ne, &skip); - __ str(result_register(), MemOperand(fp, SlotOffset(slot))); - break; - case Slot::CONTEXT: - case Slot::LOOKUP: - __ push(r0); - __ mov(r0, Operand(slot->var()->name())); - __ Push(cp, r0); // Context and name. - __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); - break; + // Const initializers need a write barrier. + ASSERT(!var->IsParameter()); // No const parameters. + if (var->IsStackLocal()) { + Label skip; + __ ldr(r1, StackOperand(var)); + __ CompareRoot(r1, Heap::kTheHoleValueRootIndex); + __ b(ne, &skip); + __ str(result_register(), StackOperand(var)); + __ bind(&skip); + } else { + ASSERT(var->IsContextSlot() || var->IsLookupSlot()); + // Like var declarations, const declarations are hoisted to function + // scope. However, unlike var initializers, const initializers are + // able to drill a hole to that function context, even from inside a + // 'with' context. We thus bypass the normal static scope lookup for + // var->IsContextSlot(). + __ push(r0); + __ mov(r0, Operand(var->name())); + __ Push(cp, r0); // Context and name. + __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); } - __ bind(&skip); } else if (var->mode() == Variable::LET && op != Token::INIT_LET) { - // Perform the assignment for non-const variables. Const assignments - // are simply skipped. - Slot* slot = var->AsSlot(); - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: { - Label assign; - // Check for an initialized let binding. - __ ldr(r1, MemOperand(fp, SlotOffset(slot))); - __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(r1, ip); - __ b(ne, &assign); - __ mov(r1, Operand(var->name())); - __ push(r1); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - // Perform the assignment. - __ bind(&assign); - __ str(result_register(), MemOperand(fp, SlotOffset(slot))); - break; - } - case Slot::CONTEXT: { - // Let variables may be the hole value if they have not been - // initialized. Throw a type error in this case. - Label assign; - MemOperand target = EmitSlotSearch(slot, r1); - // Check for an initialized let binding. - __ ldr(r3, target); - __ LoadRoot(ip, Heap::kTheHoleValueRootIndex); - __ cmp(r3, ip); - __ b(ne, &assign); - __ mov(r3, Operand(var->name())); - __ push(r3); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - // Perform the assignment. - __ bind(&assign); - __ str(result_register(), target); + // Non-initializing assignment to let variable needs a write barrier. + if (var->IsLookupSlot()) { + __ push(r0); // Value. + __ mov(r1, Operand(var->name())); + __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); + __ Push(cp, r1, r0); // Context, name, strict mode. + __ CallRuntime(Runtime::kStoreContextSlot, 4); + } else { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + Label assign; + MemOperand location = VarOperand(var, r1); + __ ldr(r3, location); + __ CompareRoot(r3, Heap::kTheHoleValueRootIndex); + __ b(ne, &assign); + __ mov(r3, Operand(var->name())); + __ push(r3); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + // Perform the assignment. + __ bind(&assign); + __ str(result_register(), location); + if (var->IsContextSlot()) { // RecordWrite may destroy all its register arguments. __ mov(r3, result_register()); - int offset = Context::SlotOffset(slot->index()); + int offset = Context::SlotOffset(var->index()); __ RecordWrite(r1, Operand(offset), r2, r3); - break; } - case Slot::LOOKUP: - // Call the runtime for the assignment. - __ push(r0); // Value. - __ mov(r1, Operand(slot->var()->name())); - __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); - __ Push(cp, r1, r0); // Context, name, strict mode. - __ CallRuntime(Runtime::kStoreContextSlot, 4); - break; } } else if (var->mode() != Variable::CONST) { - // Perform the assignment for non-const variables. Const assignments - // are simply skipped. - Slot* slot = var->AsSlot(); - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: - // Perform the assignment. - __ str(result_register(), MemOperand(fp, SlotOffset(slot))); - break; - - case Slot::CONTEXT: { - MemOperand target = EmitSlotSearch(slot, r1); - // Perform the assignment and issue the write barrier. - __ str(result_register(), target); - // RecordWrite may destroy all its register arguments. - __ mov(r3, result_register()); - int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; - __ RecordWrite(r1, Operand(offset), r2, r3); - break; + // Assignment to var or initializing assignment to let. + if (var->IsStackAllocated() || var->IsContextSlot()) { + MemOperand location = VarOperand(var, r1); + if (FLAG_debug_code && op == Token::INIT_LET) { + // Check for an uninitialized let binding. + __ ldr(r2, location); + __ CompareRoot(r2, Heap::kTheHoleValueRootIndex); + __ Check(eq, "Let binding re-initialization."); } - - case Slot::LOOKUP: - // Call the runtime for the assignment. - __ push(r0); // Value. - __ mov(r1, Operand(slot->var()->name())); - __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); - __ Push(cp, r1, r0); // Context, name, strict mode. - __ CallRuntime(Runtime::kStoreContextSlot, 4); - break; + // Perform the assignment. + __ str(r0, location); + if (var->IsContextSlot()) { + __ mov(r3, r0); + __ RecordWrite(r1, Operand(Context::SlotOffset(var->index())), r2, r3); + } + } else { + ASSERT(var->IsLookupSlot()); + __ push(r0); // Value. + __ mov(r1, Operand(var->name())); + __ mov(r0, Operand(Smi::FromInt(strict_mode_flag()))); + __ Push(cp, r1, r0); // Context, name, strict mode. + __ CallRuntime(Runtime::kStoreContextSlot, 4); } } + // Non-initializing assignments to consts are ignored. } @@ -2087,9 +2028,8 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, // 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. @@ -2120,9 +2060,8 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, // 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); @@ -2143,8 +2082,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { } // 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. @@ -2167,8 +2105,13 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, int receiver_offset = 2 + info_->scope()->num_parameters(); __ ldr(r1, MemOperand(fp, receiver_offset * kPointerSize)); __ push(r1); - // Push the strict mode flag. - __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); + // Push the strict mode flag. In harmony mode every eval call + // is a strict mode eval call. + StrictModeFlag strict_mode = strict_mode_flag(); + if (FLAG_harmony_block_scoping) { + strict_mode = kStrictMode; + } + __ mov(r1, Operand(Smi::FromInt(strict_mode))); __ push(r1); __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP @@ -2185,10 +2128,11 @@ void FullCodeGenerator::VisitCall(Call* expr) { #endif Comment cmnt(masm_, "[ Call"); - Expression* fun = expr->expression(); - Variable* var = fun->AsVariableProxy()->AsVariable(); + Expression* callee = expr->expression(); + VariableProxy* proxy = callee->AsVariableProxy(); + Property* property = callee->AsProperty(); - if (var != NULL && var->is_possibly_eval()) { + if (proxy != NULL && proxy->var()->is_possibly_eval()) { // In a call to eval, we first call %ResolvePossiblyDirectEval to // resolve the function we need to call and the receiver of the // call. Then we call the resolved function using the given @@ -2197,7 +2141,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { int arg_count = args->length(); { PreservePositionScope pos_scope(masm()->positions_recorder()); - VisitForStackValue(fun); + VisitForStackValue(callee); __ LoadRoot(r2, Heap::kUndefinedValueRootIndex); __ push(r2); // Reserved receiver slot. @@ -2211,11 +2155,10 @@ void FullCodeGenerator::VisitCall(Call* expr) { // in generated code. If we succeed, there is no need to perform a // context lookup in the runtime system. Label done; - if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) { + Variable* var = proxy->var(); + if (!var->IsUnallocated() && var->mode() == Variable::DYNAMIC_GLOBAL) { Label slow; - EmitLoadGlobalSlotCheckExtensions(var->AsSlot(), - NOT_INSIDE_TYPEOF, - &slow); + EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow); // Push the function and resolve eval. __ push(r0); EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); @@ -2223,14 +2166,12 @@ void FullCodeGenerator::VisitCall(Call* expr) { __ bind(&slow); } - // Push copy of the function (found below the arguments) and + // Push a copy of the function (found below the arguments) and // resolve eval. __ ldr(r1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ push(r1); EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); - if (done.is_linked()) { - __ bind(&done); - } + __ bind(&done); // The runtime call returns a pair of values in r0 (function) and // r1 (receiver). Touch up the stack with the right values. @@ -2240,37 +2181,32 @@ void FullCodeGenerator::VisitCall(Call* expr) { // 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. __ ldr(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); context()->DropAndPlug(1, r0); - } else if (var != NULL && !var->is_this() && var->is_global()) { + } else if (proxy != NULL && proxy->var()->IsUnallocated()) { // Push global object as receiver for the call IC. __ ldr(r0, GlobalObjectOperand()); __ push(r0); - EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); - } else if (var != NULL && var->AsSlot() != NULL && - var->AsSlot()->type() == Slot::LOOKUP) { + EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT); + } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { // Call to a lookup slot (dynamically introduced variable). Label slow, done; { PreservePositionScope scope(masm()->positions_recorder()); // Generate code for loading from variables potentially shadowed // by eval-introduced variables. - EmitDynamicLoadFromSlotFastCase(var->AsSlot(), - NOT_INSIDE_TYPEOF, - &slow, - &done); + EmitDynamicLookupFastCase(proxy->var(), NOT_INSIDE_TYPEOF, &slow, &done); } __ bind(&slow); // Call the runtime to find the function to call (returned in r0) // and the object holding it (returned in edx). __ push(context_register()); - __ mov(r2, Operand(var->name())); + __ mov(r2, Operand(proxy->name())); __ push(r2); __ CallRuntime(Runtime::kLoadContextSlot, 2); __ Push(r0, r1); // Function, receiver. @@ -2295,26 +2231,21 @@ void FullCodeGenerator::VisitCall(Call* expr) { // by LoadContextSlot. That object could be the hole if the // receiver is implicitly the global object. EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT); - } else if (fun->AsProperty() != NULL) { - // Call to an object property. - Property* prop = fun->AsProperty(); - Literal* key = prop->key()->AsLiteral(); - if (key != NULL && key->handle()->IsSymbol()) { - // Call to a named property, use call IC. - { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(prop->obj()); - } - EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); + } else if (property != NULL) { + { PreservePositionScope scope(masm()->positions_recorder()); + VisitForStackValue(property->obj()); + } + if (property->key()->IsPropertyName()) { + EmitCallWithIC(expr, + property->key()->AsLiteral()->handle(), + RelocInfo::CODE_TARGET); } else { - // Call to a keyed property. - { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(prop->obj()); - } - EmitKeyedCallWithIC(expr, prop->key()); + EmitKeyedCallWithIC(expr, property->key()); } } else { + // Call to an arbitrary expression not handled specially above. { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(fun); + VisitForStackValue(callee); } // Load global receiver object. __ ldr(r1, GlobalObjectOperand()); @@ -2579,7 +2510,7 @@ void FullCodeGenerator::EmitIsFunction(ZoneList<Expression*>* args) { &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); @@ -3618,9 +3549,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { __ 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)); @@ -3636,32 +3565,32 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { switch (expr->op()) { case Token::DELETE: { Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); - Property* prop = expr->expression()->AsProperty(); - Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); + Property* property = expr->expression()->AsProperty(); + VariableProxy* proxy = expr->expression()->AsVariableProxy(); - if (prop != NULL) { - VisitForStackValue(prop->obj()); - VisitForStackValue(prop->key()); + if (property != NULL) { + VisitForStackValue(property->obj()); + VisitForStackValue(property->key()); __ mov(r1, Operand(Smi::FromInt(strict_mode_flag()))); __ push(r1); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); context()->Plug(r0); - } else if (var != NULL) { + } else if (proxy != NULL) { + Variable* var = proxy->var(); // Delete of an unqualified identifier is disallowed in strict mode - // but "delete this" is. + // but "delete this" is allowed. ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this()); - if (var->is_global()) { + if (var->IsUnallocated()) { __ ldr(r2, GlobalObjectOperand()); __ mov(r1, Operand(var->name())); __ mov(r0, Operand(Smi::FromInt(kNonStrictMode))); __ Push(r2, r1, r0); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); context()->Plug(r0); - } else if (var->AsSlot() != NULL && - var->AsSlot()->type() != Slot::LOOKUP) { + } else if (var->IsStackAllocated() || var->IsContextSlot()) { // Result of deleting non-global, non-dynamic variables is false. // The subexpression does not have side effects. - context()->Plug(false); + context()->Plug(var->is_this()); } else { // Non-global variable. Call the runtime to try to delete from the // context where the variable was introduced. @@ -3936,7 +3865,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { ASSERT(!context()->IsEffect()); ASSERT(!context()->IsTest()); VariableProxy* proxy = expr->AsVariableProxy(); - if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { + if (proxy != NULL && proxy->var()->IsUnallocated()) { Comment cmnt(masm_, "Global variable"); __ ldr(r0, GlobalObjectOperand()); __ mov(r2, Operand(proxy->name())); @@ -3946,15 +3875,12 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { __ Call(ic); PrepareForBailout(expr, TOS_REG); context()->Plug(r0); - } else if (proxy != NULL && - proxy->var()->AsSlot() != NULL && - proxy->var()->AsSlot()->type() == Slot::LOOKUP) { + } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { Label done, slow; // Generate code for loading from variables potentially shadowed // by eval-introduced variables. - Slot* slot = proxy->var()->AsSlot(); - EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done); + EmitDynamicLookupFastCase(proxy->var(), INSIDE_TYPEOF, &slow, &done); __ bind(&slow); __ mov(r0, Operand(proxy->name())); diff --git a/src/arm/ic-arm.cc b/src/arm/ic-arm.cc index 6bad5ac0..2e49cae9 100644 --- a/src/arm/ic-arm.cc +++ b/src/arm/ic-arm.cc @@ -146,7 +146,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, StringDictionary::kElementsStartIndex * kPointerSize; const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; __ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset)); - __ tst(scratch1, Operand(PropertyDetails::TypeField::mask() << kSmiTagSize)); + __ tst(scratch1, Operand(PropertyDetails::TypeField::kMask << kSmiTagSize)); __ b(ne, miss); // Get the value at the masked, scaled index and return. @@ -194,9 +194,9 @@ static void GenerateDictionaryStore(MacroAssembler* masm, const int kElementsStartOffset = StringDictionary::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize; const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; - const int kTypeAndReadOnlyMask - = (PropertyDetails::TypeField::mask() | - PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize; + const int kTypeAndReadOnlyMask = + (PropertyDetails::TypeField::kMask | + PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize; __ ldr(scratch1, FieldMemOperand(scratch2, kDetailsOffset)); __ tst(scratch1, Operand(kTypeAndReadOnlyMask)); __ b(ne, miss); @@ -393,7 +393,6 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Probe the stub cache. Code::Flags flags = Code::ComputeFlags(kind, - NOT_IN_LOOP, MONOMORPHIC, extra_ic_state, NORMAL, @@ -734,9 +733,8 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { // ----------------------------------- // 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 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm, // ----------------------------------- // 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-arm.cc b/src/arm/lithium-arm.cc index 6292ff85..30ccd05b 100644 --- a/src/arm/lithium-arm.cc +++ b/src/arm/lithium-arm.cc @@ -311,13 +311,13 @@ void LCallKeyed::PrintDataTo(StringStream* stream) { void LCallNamed::PrintDataTo(StringStream* stream) { - SmartPointer<char> name_string = name()->ToCString(); + SmartArrayPointer<char> name_string = name()->ToCString(); stream->Add("%s #%d / ", *name_string, arity()); } void LCallGlobal::PrintDataTo(StringStream* stream) { - SmartPointer<char> name_string = name()->ToCString(); + SmartArrayPointer<char> name_string = name()->ToCString(); stream->Add("%s #%d / ", *name_string, arity()); } @@ -546,7 +546,8 @@ LChunk* LChunkBuilder::Build() { void LChunkBuilder::Abort(const char* format, ...) { if (FLAG_trace_bailout) { - SmartPointer<char> name(info()->shared_info()->DebugName()->ToCString()); + SmartArrayPointer<char> name( + info()->shared_info()->DebugName()->ToCString()); PrintF("Aborting LChunk building in @\"%s\": ", *name); va_list arguments; va_start(arguments, format); @@ -710,9 +711,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble( LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { HEnvironment* hydrogen_env = current_block_->last_environment(); - int argument_index_accumulator = 0; - instr->set_environment(CreateEnvironment(hydrogen_env, - &argument_index_accumulator)); + instr->set_environment(CreateEnvironment(hydrogen_env)); return instr; } @@ -995,13 +994,10 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { } -LEnvironment* LChunkBuilder::CreateEnvironment( - HEnvironment* hydrogen_env, - int* argument_index_accumulator) { +LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) { if (hydrogen_env == NULL) return NULL; - LEnvironment* outer = - CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); + LEnvironment* outer = CreateEnvironment(hydrogen_env->outer()); int ast_id = hydrogen_env->ast_id(); ASSERT(ast_id != AstNode::kNoNumber); int value_count = hydrogen_env->length(); @@ -1011,6 +1007,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment( argument_count_, value_count, outer); + int argument_index = 0; for (int i = 0; i < value_count; ++i) { if (hydrogen_env->is_special_index(i)) continue; @@ -1019,7 +1016,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment( if (value->IsArgumentsObject()) { op = NULL; } else if (value->IsPushArgument()) { - op = new LArgument((*argument_index_accumulator)++); + op = new LArgument(argument_index++); } else { op = UseAny(value); } @@ -1864,15 +1861,15 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastDoubleElement( LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( HLoadKeyedSpecializedArrayElement* instr) { - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); Representation representation(instr->representation()); ASSERT( (representation.IsInteger32() && - (elements_kind != JSObject::EXTERNAL_FLOAT_ELEMENTS) && - (elements_kind != JSObject::EXTERNAL_DOUBLE_ELEMENTS)) || + (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && + (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || (representation.IsDouble() && - ((elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) || - (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS)))); + ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || + (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->key()->representation().IsInteger32()); LOperand* external_pointer = UseRegister(instr->external_pointer()); LOperand* key = UseRegisterOrConstant(instr->key()); @@ -1881,7 +1878,7 @@ LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( LInstruction* load_instr = DefineAsRegister(result); // An unsigned int array load might overflow and cause a deopt, make sure it // has an environment. - return (elements_kind == JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS) ? + return (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) ? AssignEnvironment(load_instr) : load_instr; } @@ -1932,21 +1929,21 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastDoubleElement( LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( HStoreKeyedSpecializedArrayElement* instr) { Representation representation(instr->value()->representation()); - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); ASSERT( (representation.IsInteger32() && - (elements_kind != JSObject::EXTERNAL_FLOAT_ELEMENTS) && - (elements_kind != JSObject::EXTERNAL_DOUBLE_ELEMENTS)) || + (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && + (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || (representation.IsDouble() && - ((elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) || - (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS)))); + ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || + (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->external_pointer()->representation().IsExternal()); ASSERT(instr->key()->representation().IsInteger32()); LOperand* external_pointer = UseRegister(instr->external_pointer()); bool val_is_temp_register = - elements_kind == JSObject::EXTERNAL_PIXEL_ELEMENTS || - elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS; + elements_kind == EXTERNAL_PIXEL_ELEMENTS || + elements_kind == EXTERNAL_FLOAT_ELEMENTS; LOperand* val = val_is_temp_register ? UseTempRegister(instr->value()) : UseRegister(instr->value()); diff --git a/src/arm/lithium-arm.h b/src/arm/lithium-arm.h index e14e6fc7..8c18760f 100644 --- a/src/arm/lithium-arm.h +++ b/src/arm/lithium-arm.h @@ -1158,7 +1158,7 @@ class LLoadKeyedSpecializedArrayElement: public LTemplateInstruction<1, 2, 0> { LOperand* external_pointer() { return inputs_[0]; } LOperand* key() { return inputs_[1]; } - JSObject::ElementsKind elements_kind() const { + ElementsKind elements_kind() const { return hydrogen()->elements_kind(); } }; @@ -1662,7 +1662,7 @@ class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> { LOperand* external_pointer() { return inputs_[0]; } LOperand* key() { return inputs_[1]; } LOperand* value() { return inputs_[2]; } - JSObject::ElementsKind elements_kind() const { + ElementsKind elements_kind() const { return hydrogen()->elements_kind(); } }; @@ -2159,8 +2159,7 @@ class LChunkBuilder BASE_EMBEDDED { LInstruction* instr, int ast_id); void ClearInstructionPendingDeoptimizationEnvironment(); - LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, - int* argument_index_accumulator); + LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env); void VisitInstruction(HInstruction* current); diff --git a/src/arm/lithium-codegen-arm.cc b/src/arm/lithium-codegen-arm.cc index 976576be..f5d74491 100644 --- a/src/arm/lithium-codegen-arm.cc +++ b/src/arm/lithium-codegen-arm.cc @@ -101,7 +101,8 @@ void LCodeGen::FinishCode(Handle<Code> code) { void LCodeGen::Abort(const char* format, ...) { if (FLAG_trace_bailout) { - SmartPointer<char> name(info()->shared_info()->DebugName()->ToCString()); + SmartArrayPointer<char> name( + info()->shared_info()->DebugName()->ToCString()); PrintF("Aborting LCodeGen in @\"%s\": ", *name); va_list arguments; va_start(arguments, format); @@ -198,14 +199,14 @@ bool LCodeGen::GeneratePrologue() { // Copy any necessary parameters into the context. int num_parameters = scope()->num_parameters(); for (int i = 0; i < num_parameters; i++) { - Slot* slot = scope()->parameter(i)->AsSlot(); - if (slot != NULL && slot->type() == Slot::CONTEXT) { + Variable* var = scope()->parameter(i); + if (var->IsContextSlot()) { int parameter_offset = StandardFrameConstants::kCallerSPOffset + (num_parameters - 1 - i) * kPointerSize; // Load parameter from stack. __ ldr(r0, MemOperand(fp, parameter_offset)); // Store it in the context. - __ mov(r1, Operand(Context::SlotOffset(slot->index()))); + __ mov(r1, Operand(Context::SlotOffset(var->index()))); __ str(r0, MemOperand(cp, r1)); // Update the write barrier. This clobbers all involved // registers, so we have to use two more registers to avoid @@ -2410,11 +2411,11 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) { __ ldr(scratch, FieldMemOperand(scratch, Map::kBitField2Offset)); __ ubfx(scratch, scratch, Map::kElementsKindShift, Map::kElementsKindBitCount); - __ cmp(scratch, Operand(JSObject::FAST_ELEMENTS)); + __ cmp(scratch, Operand(FAST_ELEMENTS)); __ b(eq, &done); - __ cmp(scratch, Operand(JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND)); + __ cmp(scratch, Operand(FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND)); __ b(lt, &fail); - __ cmp(scratch, Operand(JSObject::LAST_EXTERNAL_ARRAY_ELEMENTS_KIND)); + __ cmp(scratch, Operand(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND)); __ b(le, &done); __ bind(&fail); __ Abort("Check for fast or external elements failed."); @@ -2478,7 +2479,7 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( Register scratch = scratch0(); int shift_size = - ElementsKindToShiftSize(JSObject::FAST_DOUBLE_ELEMENTS); + ElementsKindToShiftSize(FAST_DOUBLE_ELEMENTS); int constant_key = 0; if (key_is_constant) { constant_key = ToInteger32(LConstantOperand::cast(instr->key())); @@ -2515,7 +2516,7 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( LLoadKeyedSpecializedArrayElement* instr) { Register external_pointer = ToRegister(instr->external_pointer()); Register key = no_reg; - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); bool key_is_constant = instr->key()->IsConstantOperand(); int constant_key = 0; if (key_is_constant) { @@ -2528,18 +2529,18 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( } int shift_size = ElementsKindToShiftSize(elements_kind); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS || - elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS || + elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { CpuFeatures::Scope scope(VFP3); DwVfpRegister result = ToDoubleRegister(instr->result()); Operand operand = key_is_constant ? Operand(constant_key * (1 << shift_size)) : Operand(key, LSL, shift_size); __ add(scratch0(), external_pointer, operand); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { __ vldr(result.low(), scratch0(), 0); __ vcvt_f64_f32(result, result.low()); - } else { // i.e. elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS + } else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS __ vldr(result, scratch0(), 0); } } else { @@ -2548,23 +2549,23 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( ? MemOperand(external_pointer, constant_key * (1 << shift_size)) : MemOperand(external_pointer, key, LSL, shift_size)); switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: __ ldrsb(result, mem_operand); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ ldrb(result, mem_operand); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: __ ldrsh(result, mem_operand); break; - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ ldrh(result, mem_operand); break; - case JSObject::EXTERNAL_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: __ ldr(result, mem_operand); break; - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ ldr(result, mem_operand); __ cmp(result, Operand(0x80000000)); // TODO(danno): we could be more clever here, perhaps having a special @@ -2572,12 +2573,12 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( // happens, and generate code that returns a double rather than int. DeoptimizeIf(cs, instr->environment()); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -3177,7 +3178,7 @@ void LCodeGen::DoCallKeyed(LCallKeyed* instr) { 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)); } @@ -3189,7 +3190,7 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) { 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. @@ -3201,7 +3202,7 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) { 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)); @@ -3214,7 +3215,7 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) { 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)); @@ -3340,7 +3341,7 @@ void LCodeGen::DoStoreKeyedFastDoubleElement( } else { key = ToRegister(instr->key()); } - int shift_size = ElementsKindToShiftSize(JSObject::FAST_DOUBLE_ELEMENTS); + int shift_size = ElementsKindToShiftSize(FAST_DOUBLE_ELEMENTS); Operand operand = key_is_constant ? Operand(constant_key * (1 << shift_size) + FixedDoubleArray::kHeaderSize - kHeapObjectTag) @@ -3367,7 +3368,7 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( Register external_pointer = ToRegister(instr->external_pointer()); Register key = no_reg; - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); bool key_is_constant = instr->key()->IsConstantOperand(); int constant_key = 0; if (key_is_constant) { @@ -3380,17 +3381,17 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( } int shift_size = ElementsKindToShiftSize(elements_kind); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS || - elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS || + elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { CpuFeatures::Scope scope(VFP3); DwVfpRegister value(ToDoubleRegister(instr->value())); Operand operand(key_is_constant ? Operand(constant_key * (1 << shift_size)) : Operand(key, LSL, shift_size)); __ add(scratch0(), external_pointer, operand); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { __ vcvt_f32_f64(double_scratch0().low(), value); __ vstr(double_scratch0().low(), scratch0(), 0); - } else { // i.e. elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS + } else { // i.e. elements_kind == EXTERNAL_DOUBLE_ELEMENTS __ vstr(value, scratch0(), 0); } } else { @@ -3399,25 +3400,25 @@ void LCodeGen::DoStoreKeyedSpecializedArrayElement( ? MemOperand(external_pointer, constant_key * (1 << shift_size)) : MemOperand(external_pointer, key, LSL, shift_size)); switch (elements_kind) { - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ strb(value, mem_operand); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ strh(value, mem_operand); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ str(value, mem_operand); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -3509,7 +3510,8 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { // Dispatch on the encoding: ASCII or two-byte. Label ascii_string; - STATIC_ASSERT(kAsciiStringTag != 0); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ tst(result, Operand(kStringEncodingMask)); __ b(ne, &ascii_string); diff --git a/src/arm/lithium-gap-resolver-arm.h b/src/arm/lithium-gap-resolver-arm.h index 334d2920..9dd09c8d 100644 --- a/src/arm/lithium-gap-resolver-arm.h +++ b/src/arm/lithium-gap-resolver-arm.h @@ -40,7 +40,6 @@ class LGapResolver; class LGapResolver BASE_EMBEDDED { public: - explicit LGapResolver(LCodeGen* owner); // Resolve a set of parallel moves, emitting assembler instructions. diff --git a/src/arm/macro-assembler-arm.cc b/src/arm/macro-assembler-arm.cc index 88477bb7..f37f3102 100644 --- a/src/arm/macro-assembler-arm.cc +++ b/src/arm/macro-assembler-arm.cc @@ -760,9 +760,9 @@ void MacroAssembler::EnterExitFrame(bool save_doubles, int stack_space) { str(ip, MemOperand(fp, ExitFrameConstants::kCodeOffset)); // Save the frame pointer and the context in top. - mov(ip, Operand(ExternalReference(Isolate::k_c_entry_fp_address, isolate()))); + mov(ip, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate()))); str(fp, MemOperand(ip)); - mov(ip, Operand(ExternalReference(Isolate::k_context_address, isolate()))); + mov(ip, Operand(ExternalReference(Isolate::kContextAddress, isolate()))); str(cp, MemOperand(ip)); // Optionally save all double registers. @@ -838,11 +838,11 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles, // Clear top frame. mov(r3, Operand(0, RelocInfo::NONE)); - mov(ip, Operand(ExternalReference(Isolate::k_c_entry_fp_address, isolate()))); + mov(ip, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate()))); str(r3, MemOperand(ip)); // Restore current context from top and clear it in debug mode. - mov(ip, Operand(ExternalReference(Isolate::k_context_address, isolate()))); + mov(ip, Operand(ExternalReference(Isolate::kContextAddress, isolate()))); ldr(cp, MemOperand(ip)); #ifdef DEBUG str(r3, MemOperand(ip)); @@ -1118,7 +1118,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, } stm(db_w, sp, r3.bit() | cp.bit() | fp.bit() | lr.bit()); // Save the current handler as the next handler. - mov(r3, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); + mov(r3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); ldr(r1, MemOperand(r3)); push(r1); // Link this handler as the new current one. @@ -1134,7 +1134,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, mov(r7, Operand(0, RelocInfo::NONE)); // NULL frame pointer. stm(db_w, sp, r5.bit() | r6.bit() | r7.bit() | lr.bit()); // Save the current handler as the next handler. - mov(r7, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); + mov(r7, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); ldr(r6, MemOperand(r7)); push(r6); // Link this handler as the new current one. @@ -1146,7 +1146,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, void MacroAssembler::PopTryHandler() { STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); pop(r1); - mov(ip, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); + mov(ip, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); add(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize)); str(r1, MemOperand(ip)); } @@ -1166,7 +1166,7 @@ void MacroAssembler::Throw(Register value) { } // Drop the sp to the top of the handler. - mov(r3, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); + mov(r3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); ldr(sp, MemOperand(r3)); // Restore the next handler. @@ -1206,7 +1206,7 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, } // Drop sp to the top stack handler. - mov(r3, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); + mov(r3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); ldr(sp, MemOperand(r3)); // Unwind the handlers until the ENTRY handler is found. @@ -1230,7 +1230,7 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, if (type == OUT_OF_MEMORY) { // Set external caught exception to false. ExternalReference external_caught( - Isolate::k_external_caught_exception_address, isolate()); + Isolate::kExternalCaughtExceptionAddress, isolate()); mov(r0, Operand(false, RelocInfo::NONE)); mov(r2, Operand(external_caught)); str(r0, MemOperand(r2)); @@ -1238,7 +1238,7 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, // Set pending exception and r0 to out of memory exception. Failure* out_of_memory = Failure::OutOfMemoryException(); mov(r0, Operand(reinterpret_cast<int32_t>(out_of_memory))); - mov(r2, Operand(ExternalReference(Isolate::k_pending_exception_address, + mov(r2, Operand(ExternalReference(Isolate::kPendingExceptionAddress, isolate()))); str(r0, MemOperand(r2)); } @@ -1421,7 +1421,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, const int kDetailsOffset = NumberDictionary::kElementsStartOffset + 2 * kPointerSize; ldr(t1, FieldMemOperand(t2, kDetailsOffset)); - tst(t1, Operand(Smi::FromInt(PropertyDetails::TypeField::mask()))); + tst(t1, Operand(Smi::FromInt(PropertyDetails::TypeField::kMask))); b(ne, miss); // Get the value at the masked, scaled index and return. @@ -1725,6 +1725,46 @@ void MacroAssembler::AllocateAsciiConsString(Register result, } +void MacroAssembler::AllocateTwoByteSlicedString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required) { + AllocateInNewSpace(SlicedString::kSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + InitializeNewString(result, + length, + Heap::kSlicedStringMapRootIndex, + scratch1, + scratch2); +} + + +void MacroAssembler::AllocateAsciiSlicedString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required) { + AllocateInNewSpace(SlicedString::kSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + InitializeNewString(result, + length, + Heap::kSlicedAsciiStringMapRootIndex, + scratch1, + scratch2); +} + + void MacroAssembler::CompareObjectType(Register object, Register map, Register type_reg, @@ -1753,7 +1793,7 @@ void MacroAssembler::CompareRoot(Register obj, void MacroAssembler::CheckFastElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(JSObject::FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 0); ldrb(scratch, FieldMemOperand(map, Map::kBitField2Offset)); cmp(scratch, Operand(Map::kMaximumBitField2FastElementValue)); b(hi, fail); diff --git a/src/arm/macro-assembler-arm.h b/src/arm/macro-assembler-arm.h index 9c653adb..6084fde2 100644 --- a/src/arm/macro-assembler-arm.h +++ b/src/arm/macro-assembler-arm.h @@ -532,6 +532,16 @@ class MacroAssembler: public Assembler { Register scratch1, Register scratch2, Label* gc_required); + void AllocateTwoByteSlicedString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required); + void AllocateAsciiSlicedString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required); // Allocates a heap number or jumps to the gc_required label if the young // space is full and a scavenge is needed. All registers are clobbered also @@ -586,9 +596,7 @@ class MacroAssembler: public Assembler { // 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/arm/regexp-macro-assembler-arm.h b/src/arm/regexp-macro-assembler-arm.h index 0e653868..5c8ed069 100644 --- a/src/arm/regexp-macro-assembler-arm.h +++ b/src/arm/regexp-macro-assembler-arm.h @@ -116,6 +116,7 @@ class RegExpMacroAssemblerARM: public NativeRegExpMacroAssembler { static int CheckStackGuardState(Address* return_address, Code* re_code, Address re_frame); + private: // Offsets from frame_pointer() of function parameters and stored registers. static const int kFramePointer = 0; diff --git a/src/arm/stub-cache-arm.cc b/src/arm/stub-cache-arm.cc index 53458929..f8565924 100644 --- a/src/arm/stub-cache-arm.cc +++ b/src/arm/stub-cache-arm.cc @@ -3099,7 +3099,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { // -- r1 : receiver // ----------------------------------- Code* stub; - JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + ElementsKind elements_kind = receiver_map->elements_kind(); MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(r1, @@ -3193,7 +3193,7 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { // -- r3 : scratch // ----------------------------------- Code* stub; - JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + ElementsKind elements_kind = receiver_map->elements_kind(); bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; MaybeObject* maybe_stub = KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); @@ -3438,25 +3438,25 @@ void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( } -static bool IsElementTypeSigned(JSObject::ElementsKind elements_kind) { +static bool IsElementTypeSigned(ElementsKind elements_kind) { switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_INT_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: return true; - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: return false; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); return false; } @@ -3466,7 +3466,7 @@ static bool IsElementTypeSigned(JSObject::ElementsKind elements_kind) { void KeyedLoadStubCompiler::GenerateLoadExternalArray( MacroAssembler* masm, - JSObject::ElementsKind elements_kind) { + ElementsKind elements_kind) { // ---------- S t a t e -------------- // -- lr : return address // -- r0 : key @@ -3501,24 +3501,24 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( Register value = r2; switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: __ ldrsb(value, MemOperand(r3, key, LSR, 1)); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ ldrb(value, MemOperand(r3, key, LSR, 1)); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: __ ldrsh(value, MemOperand(r3, key, LSL, 0)); break; - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ ldrh(value, MemOperand(r3, key, LSL, 0)); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ ldr(value, MemOperand(r3, key, LSL, 1)); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: if (CpuFeatures::IsSupported(VFP3)) { CpuFeatures::Scope scope(VFP3); __ add(r2, r3, Operand(key, LSL, 1)); @@ -3527,7 +3527,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ ldr(value, MemOperand(r3, key, LSL, 1)); } break; - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: if (CpuFeatures::IsSupported(VFP3)) { CpuFeatures::Scope scope(VFP3); __ add(r2, r3, Operand(key, LSL, 2)); @@ -3539,10 +3539,10 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ ldr(r3, MemOperand(r4, Register::kSizeInBytes)); } break; - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -3556,7 +3556,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( // d0: value (if VFP3 is supported) // r2/r3: value (if VFP3 is not supported) - if (elements_kind == JSObject::EXTERNAL_INT_ELEMENTS) { + if (elements_kind == EXTERNAL_INT_ELEMENTS) { // For the Int and UnsignedInt array types, we need to see whether // the value can be represented in a Smi. If not, we need to convert // it to a HeapNumber. @@ -3600,7 +3600,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ str(dst2, FieldMemOperand(r0, HeapNumber::kExponentOffset)); __ Ret(); } - } else if (elements_kind == JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS) { + } else if (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) { // The test is different for unsigned int values. Since we need // the value to be in the range of a positive smi, we can't // handle either of the top two bits being set in the value. @@ -3665,7 +3665,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ mov(r0, r4); __ Ret(); } - } else if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + } else if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { // For the floating-point array type, we need to always allocate a // HeapNumber. if (CpuFeatures::IsSupported(VFP3)) { @@ -3735,7 +3735,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ mov(r0, r3); __ Ret(); } - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { if (CpuFeatures::IsSupported(VFP3)) { CpuFeatures::Scope scope(VFP3); // Allocate a HeapNumber for the result. Don't use r0 and r1 as @@ -3792,7 +3792,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( void KeyedStoreStubCompiler::GenerateStoreExternalArray( MacroAssembler* masm, - JSObject::ElementsKind elements_kind) { + ElementsKind elements_kind) { // ---------- S t a t e -------------- // -- r0 : value // -- r1 : key @@ -3824,7 +3824,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // Handle both smis and HeapNumbers in the fast path. Go to the // runtime for all other kinds of values. // r3: external array. - if (elements_kind == JSObject::EXTERNAL_PIXEL_ELEMENTS) { + if (elements_kind == EXTERNAL_PIXEL_ELEMENTS) { // Double to pixel conversion is only implemented in the runtime for now. __ JumpIfNotSmi(value, &slow); } else { @@ -3836,29 +3836,29 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // r3: base pointer of external storage. // r5: value (integer). switch (elements_kind) { - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: // Clamp the value to [0..255]. __ Usat(r5, 8, Operand(r5)); __ strb(r5, MemOperand(r3, key, LSR, 1)); break; - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ strb(r5, MemOperand(r3, key, LSR, 1)); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ strh(r5, MemOperand(r3, key, LSL, 0)); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ str(r5, MemOperand(r3, key, LSL, 1)); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: // Perform int-to-float conversion and store to memory. __ SmiUntag(r4, key); StoreIntAsFloat(masm, r3, r4, r5, r6, r7, r9); break; - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: __ add(r3, r3, Operand(key, LSL, 2)); // r3: effective address of the double element FloatingPointHelper::Destination destination; @@ -3879,10 +3879,10 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ str(r7, MemOperand(r3, Register::kSizeInBytes)); } break; - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -3890,7 +3890,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // Entry registers are intact, r0 holds the value which is the return value. __ Ret(); - if (elements_kind != JSObject::EXTERNAL_PIXEL_ELEMENTS) { + if (elements_kind != EXTERNAL_PIXEL_ELEMENTS) { // r3: external array. __ bind(&check_heap_number); __ CompareObjectType(value, r5, r6, HEAP_NUMBER_TYPE); @@ -3906,7 +3906,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( if (CpuFeatures::IsSupported(VFP3)) { CpuFeatures::Scope scope(VFP3); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { // vldr requires offset to be a multiple of 4 so we can not // include -kHeapObjectTag into it. __ sub(r5, r0, Operand(kHeapObjectTag)); @@ -3914,7 +3914,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ add(r5, r3, Operand(key, LSL, 1)); __ vcvt_f32_f64(s0, d0); __ vstr(s0, r5, 0); - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { __ sub(r5, r0, Operand(kHeapObjectTag)); __ vldr(d0, r5, HeapNumber::kValueOffset); __ add(r5, r3, Operand(key, LSL, 2)); @@ -3927,25 +3927,25 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ EmitECMATruncate(r5, d0, s2, r6, r7, r9); switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ strb(r5, MemOperand(r3, key, LSR, 1)); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ strh(r5, MemOperand(r3, key, LSL, 0)); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ str(r5, MemOperand(r3, key, LSL, 1)); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -3959,7 +3959,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ ldr(r5, FieldMemOperand(value, HeapNumber::kExponentOffset)); __ ldr(r6, FieldMemOperand(value, HeapNumber::kMantissaOffset)); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { Label done, nan_or_infinity_or_zero; static const int kMantissaInHiWordShift = kBinary32MantissaBits - HeapNumber::kMantissaBitsInTopWord; @@ -4011,7 +4011,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ orr(r9, r9, Operand(r5, LSL, kMantissaInHiWordShift)); __ orr(r5, r9, Operand(r6, LSR, kMantissaInLoWordShift)); __ b(&done); - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { __ add(r7, r3, Operand(key, LSL, 2)); // r7: effective address of destination element. __ str(r6, MemOperand(r7, 0)); @@ -4066,25 +4066,25 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ bind(&done); switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ strb(r5, MemOperand(r3, key, LSR, 1)); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ strh(r5, MemOperand(r3, key, LSL, 0)); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ str(r5, MemOperand(r3, key, LSL, 1)); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } diff --git a/src/array.js b/src/array.js index f73b0c1b..98fe3ac7 100644 --- a/src/array.js +++ b/src/array.js @@ -208,7 +208,7 @@ function ConvertToLocaleString(e) { // 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 @@ function ArraySort(comparefn) { // 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 @@ function ArrayFilter(f, receiver) { ["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 @@ function ArrayForEach(f, receiver) { ["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 @@ function ArraySome(f, receiver) { ["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 @@ function ArrayEvery(f, receiver) { ["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 @@ function ArrayMap(f, receiver) { ["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 @@ function ArrayReduce(callback, current) { ["Array.prototype.reduce"]); } - if (!IS_FUNCTION(callback)) { + if (!IS_SPEC_FUNCTION(callback)) { throw MakeTypeError('called_non_callable', [callback]); } @@ -1281,7 +1281,7 @@ function ArrayReduceRight(callback, current) { ["Array.prototype.reduceRight"]); } - if (!IS_FUNCTION(callback)) { + if (!IS_SPEC_FUNCTION(callback)) { throw MakeTypeError('called_non_callable', [callback]); } var i = ToUint32(this.length) - 1; @@ -1314,12 +1314,13 @@ function ArrayIsArray(obj) { // ------------------------------------------------------------------- -function SetupArray() { - // Setup non-enumerable constructor property on the Array.prototype +function SetUpArray() { + %CheckIsBootstrapping(); + // Set up non-enumerable constructor property on the Array.prototype // object. %SetProperty($Array.prototype, "constructor", $Array, DONT_ENUM); - // Setup non-enumerable functions on the Array object. + // Set up non-enumerable functions on the Array object. InstallFunctions($Array, DONT_ENUM, $Array( "isArray", ArrayIsArray )); @@ -1337,7 +1338,7 @@ function SetupArray() { return f; } - // Setup non-enumerable functions of the Array.prototype object and + // Set up non-enumerable functions of the Array.prototype object and // set their names. // Manipulate the length of some of the functions to meet // expectations set by ECMA-262 or Mozilla. @@ -1368,19 +1369,13 @@ function SetupArray() { %FinishArrayPrototypeSetup($Array.prototype); // The internal Array prototype doesn't need to be fancy, since it's never - // exposed to user code, so no hidden prototypes or DONT_ENUM attributes - // are necessary. - // The null __proto__ ensures that we never inherit any user created - // getters or setters from, e.g., Object.prototype. - InternalArray.prototype.__proto__ = null; - // Adding only the functions that are actually used, and a toString. - InternalArray.prototype.join = getFunction("join", ArrayJoin); - InternalArray.prototype.pop = getFunction("pop", ArrayPop); - InternalArray.prototype.push = getFunction("push", ArrayPush); - InternalArray.prototype.toString = function() { - return "Internal Array, length " + this.length; - }; + // exposed to user code. + // Adding only the functions that are actually used. + SetUpLockedPrototype(InternalArray, $Array(), $Array( + "join", getFunction("join", ArrayJoin), + "pop", getFunction("pop", ArrayPop), + "push", getFunction("push", ArrayPush) + )); } - -SetupArray(); +SetUpArray(); @@ -36,20 +36,9 @@ namespace v8 { namespace internal { -AstSentinels::AstSentinels() - : this_proxy_(Isolate::Current(), true), - identifier_proxy_(Isolate::Current(), false), - valid_left_hand_side_sentinel_(Isolate::Current()), - this_property_(Isolate::Current(), &this_proxy_, NULL, 0), - call_sentinel_(Isolate::Current(), NULL, NULL, 0) { -} - - // ---------------------------------------------------------------------------- // All the Accept member functions for each syntax tree node type. -void Slot::Accept(AstVisitor* v) { v->VisitSlot(this); } - #define DECL_ACCEPT(type) \ void type::Accept(AstVisitor* v) { v->Visit##type(this); } AST_NODE_LIST(DECL_ACCEPT) @@ -101,15 +90,6 @@ VariableProxy::VariableProxy(Isolate* isolate, } -VariableProxy::VariableProxy(Isolate* isolate, bool is_this) - : Expression(isolate), - var_(NULL), - is_this_(is_this), - inside_with_(false), - is_trivial_(false) { -} - - void VariableProxy::BindTo(Variable* var) { ASSERT(var_ == NULL); // must be bound only once ASSERT(var != NULL); // must bind @@ -414,12 +394,6 @@ bool TargetCollector::IsInlineable() const { } -bool Slot::IsInlineable() const { - UNREACHABLE(); - return false; -} - - bool ForInStatement::IsInlineable() const { return false; } @@ -430,11 +404,6 @@ bool WithStatement::IsInlineable() const { } -bool ExitContextStatement::IsInlineable() const { - return false; -} - - bool SwitchStatement::IsInlineable() const { return false; } @@ -487,12 +456,6 @@ bool SharedFunctionInfoLiteral::IsInlineable() const { } -bool ValidLeftHandSideSentinel::IsInlineable() const { - UNREACHABLE(); - return false; -} - - bool ForStatement::IsInlineable() const { return (init() == NULL || init()->IsInlineable()) && (cond() == NULL || cond()->IsInlineable()) @@ -566,7 +529,7 @@ bool Conditional::IsInlineable() const { bool VariableProxy::IsInlineable() const { - return var()->is_global() || var()->IsStackAllocated(); + return var()->IsUnallocated() || var()->IsStackAllocated(); } @@ -1006,7 +969,7 @@ class RegExpUnparser: public RegExpVisitor { public: RegExpUnparser(); void VisitCharacterRange(CharacterRange that); - SmartPointer<const char> ToString() { return stream_.ToCString(); } + SmartArrayPointer<const char> ToString() { return stream_.ToCString(); } #define MAKE_CASE(Name) virtual void* Visit##Name(RegExp##Name*, void* data); FOR_EACH_REG_EXP_TREE_TYPE(MAKE_CASE) #undef MAKE_CASE @@ -1161,7 +1124,7 @@ void* RegExpUnparser::VisitEmpty(RegExpEmpty* that, void* data) { } -SmartPointer<const char> RegExpTree::ToString() { +SmartArrayPointer<const char> RegExpTree::ToString() { RegExpUnparser unparser; Accept(&unparser, NULL); return unparser.ToString(); @@ -62,7 +62,6 @@ namespace internal { V(BreakStatement) \ V(ReturnStatement) \ V(WithStatement) \ - V(ExitContextStatement) \ V(SwitchStatement) \ V(DoWhileStatement) \ V(WhileStatement) \ @@ -134,6 +133,10 @@ class AstNode: public ZoneObject { static const int kNoNumber = -1; static const int kFunctionEntryId = 2; // Using 0 could disguise errors. + // This AST id identifies the point after the declarations have been + // visited. We need it to capture the environment effects of declarations + // that emit code (function declarations). + static const int kDeclarationsId = 3; // Override ZoneObject's new to count allocated AST nodes. void* operator new(size_t size, Zone* zone) { @@ -161,7 +164,6 @@ class AstNode: public ZoneObject { virtual BreakableStatement* AsBreakableStatement() { return NULL; } virtual IterationStatement* AsIterationStatement() { return NULL; } virtual MaterializedLiteral* AsMaterializedLiteral() { return NULL; } - virtual Slot* AsSlot() { return NULL; } // True if the node is simple enough for us to inline calls containing it. virtual bool IsInlineable() const = 0; @@ -316,20 +318,6 @@ class Expression: public AstNode { }; -/** - * A sentinel used during pre parsing that represents some expression - * that is a valid left hand side without having to actually build - * the expression. - */ -class ValidLeftHandSideSentinel: public Expression { - public: - explicit ValidLeftHandSideSentinel(Isolate* isolate) : Expression(isolate) {} - virtual bool IsValidLeftHandSide() { return true; } - virtual void Accept(AstVisitor* v) { UNREACHABLE(); } - virtual bool IsInlineable() const; -}; - - class BreakableStatement: public Statement { public: enum Type { @@ -404,10 +392,14 @@ class Block: public BreakableStatement { class Declaration: public AstNode { public: - Declaration(VariableProxy* proxy, Variable::Mode mode, FunctionLiteral* fun) + Declaration(VariableProxy* proxy, + Variable::Mode mode, + FunctionLiteral* fun, + Scope* scope) : proxy_(proxy), mode_(mode), - fun_(fun) { + fun_(fun), + scope_(scope) { ASSERT(mode == Variable::VAR || mode == Variable::CONST || mode == Variable::LET); @@ -421,11 +413,15 @@ class Declaration: public AstNode { Variable::Mode mode() const { return mode_; } FunctionLiteral* fun() const { return fun_; } // may be NULL virtual bool IsInlineable() const; + Scope* scope() const { return scope_; } private: VariableProxy* proxy_; Variable::Mode mode_; FunctionLiteral* fun_; + + // Nested scope from which the declaration originated. + Scope* scope_; }; @@ -684,14 +680,6 @@ class WithStatement: public Statement { }; -class ExitContextStatement: public Statement { - public: - virtual bool IsInlineable() const; - - DECLARE_NODE_TYPE(ExitContextStatement) -}; - - class CaseClause: public ZoneObject { public: CaseClause(Isolate* isolate, @@ -1114,9 +1102,6 @@ class VariableProxy: public Expression { DECLARE_NODE_TYPE(VariableProxy) - // Type testing & conversion - Variable* AsVariable() { return (this == NULL) ? NULL : var_; } - virtual bool IsValidLeftHandSide() { return var_ == NULL ? true : var_->IsValidLeftHandSide(); } @@ -1133,10 +1118,7 @@ class VariableProxy: public Expression { return !is_this() && name().is_identical_to(n); } - bool IsArguments() { - Variable* variable = AsVariable(); - return (variable == NULL) ? false : variable->is_arguments(); - } + bool IsArguments() { return var_ != NULL && var_->is_arguments(); } Handle<String> name() const { return name_; } Variable* var() const { return var_; } @@ -1162,73 +1144,11 @@ class VariableProxy: public Expression { bool is_this, bool inside_with, int position = RelocInfo::kNoPosition); - VariableProxy(Isolate* isolate, bool is_this); friend class Scope; }; -class VariableProxySentinel: public VariableProxy { - public: - virtual bool IsValidLeftHandSide() { return !is_this(); } - - private: - VariableProxySentinel(Isolate* isolate, bool is_this) - : VariableProxy(isolate, is_this) { } - - friend class AstSentinels; -}; - - -class Slot: public Expression { - public: - enum Type { - // A slot in the parameter section on the stack. index() is - // the parameter index, counting left-to-right, starting at 0. - PARAMETER, - - // A slot in the local section on the stack. index() is - // the variable index in the stack frame, starting at 0. - LOCAL, - - // An indexed slot in a heap context. index() is the - // variable index in the context object on the heap, - // starting at 0. var()->scope() is the corresponding - // scope. - CONTEXT, - - // A named slot in a heap context. var()->name() is the - // variable name in the context object on the heap, - // with lookup starting at the current context. index() - // is invalid. - LOOKUP - }; - - Slot(Isolate* isolate, Variable* var, Type type, int index) - : Expression(isolate), var_(var), type_(type), index_(index) { - ASSERT(var != NULL); - } - - virtual void Accept(AstVisitor* v); - - virtual Slot* AsSlot() { return this; } - - bool IsStackAllocated() { return type_ == PARAMETER || type_ == LOCAL; } - - // Accessors - Variable* var() const { return var_; } - Type type() const { return type_; } - int index() const { return index_; } - bool is_arguments() const { return var_->is_arguments(); } - virtual bool IsInlineable() const; - - private: - Variable* var_; - Type type_; - int index_; -}; - - class Property: public Expression { public: Property(Isolate* isolate, @@ -1337,36 +1257,6 @@ class Call: public Expression { }; -class AstSentinels { - public: - ~AstSentinels() { } - - // Returns a property singleton property access on 'this'. Used - // during preparsing. - Property* this_property() { return &this_property_; } - VariableProxySentinel* this_proxy() { return &this_proxy_; } - VariableProxySentinel* identifier_proxy() { return &identifier_proxy_; } - ValidLeftHandSideSentinel* valid_left_hand_side_sentinel() { - return &valid_left_hand_side_sentinel_; - } - Call* call_sentinel() { return &call_sentinel_; } - EmptyStatement* empty_statement() { return &empty_statement_; } - - private: - AstSentinels(); - VariableProxySentinel this_proxy_; - VariableProxySentinel identifier_proxy_; - ValidLeftHandSideSentinel valid_left_hand_side_sentinel_; - Property this_property_; - Call call_sentinel_; - EmptyStatement empty_statement_; - - friend class Isolate; - - DISALLOW_COPY_AND_ASSIGN(AstSentinels); -}; - - class CallNew: public Expression { public: CallNew(Isolate* isolate, @@ -1885,7 +1775,7 @@ class RegExpTree: public ZoneObject { // expression. virtual Interval CaptureRegisters() { return Interval::Empty(); } virtual void AppendToText(RegExpText* text); - SmartPointer<const char> ToString(); + SmartArrayPointer<const char> ToString(); #define MAKE_ASTYPE(Name) \ virtual RegExp##Name* As##Name(); \ virtual bool Is##Name(); @@ -2239,9 +2129,6 @@ class AstVisitor BASE_EMBEDDED { void SetStackOverflow() { stack_overflow_ = true; } void ClearStackOverflow() { stack_overflow_ = false; } - // Nodes not appearing in the AST, including slots. - virtual void VisitSlot(Slot* node) { UNREACHABLE(); } - // Individual AST nodes. #define DEF_VISIT(type) \ virtual void Visit##type(type* node) = 0; diff --git a/src/bignum-dtoa.cc b/src/bignum-dtoa.cc index 088dd79f..a9616909 100644 --- a/src/bignum-dtoa.cc +++ b/src/bignum-dtoa.cc @@ -1,4 +1,4 @@ -// Copyright 2010 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: @@ -27,7 +27,10 @@ #include <math.h> -#include "v8.h" +#include "../include/v8stdint.h" +#include "checks.h" +#include "utils.h" + #include "bignum-dtoa.h" #include "bignum.h" diff --git a/src/bignum.h b/src/bignum.h index 1d2bff61..dcc4fa70 100644 --- a/src/bignum.h +++ b/src/bignum.h @@ -92,6 +92,7 @@ class Bignum { static bool PlusLess(const Bignum& a, const Bignum& b, const Bignum& c) { return PlusCompare(a, b, c) < 0; } + private: typedef uint32_t Chunk; typedef uint64_t DoubleChunk; diff --git a/src/bootstrapper.cc b/src/bootstrapper.cc index 9f01664d..f07e625e 100644 --- a/src/bootstrapper.cc +++ b/src/bootstrapper.cc @@ -350,7 +350,14 @@ static Handle<JSFunction> InstallFunction(Handle<JSObject> target, prototype, call_code, is_ecma_native); - SetLocalPropertyNoThrow(target, symbol, function, DONT_ENUM); + PropertyAttributes attributes; + if (target->IsJSBuiltinsObject()) { + attributes = + static_cast<PropertyAttributes>(DONT_ENUM | DONT_DELETE | READ_ONLY); + } else { + attributes = DONT_ENUM; + } + SetLocalPropertyNoThrow(target, symbol, function, attributes); if (is_ecma_native) { function->shared()->set_instance_class_name(*symbol); } @@ -1060,7 +1067,7 @@ void Genesis::InitializeGlobal(Handle<GlobalObject> inner_global, Handle<Map> new_map = factory->CopyMapDropTransitions(old_map); new_map->set_pre_allocated_property_fields(2); Handle<JSObject> result = factory->NewJSObjectFromMap(new_map); - new_map->set_elements_kind(JSObject::NON_STRICT_ARGUMENTS_ELEMENTS); + new_map->set_elements_kind(NON_STRICT_ARGUMENTS_ELEMENTS); // Set up a well-formed parameter map to make assertions happy. Handle<FixedArray> elements = factory->NewFixedArray(2); elements->set_map(heap->non_strict_arguments_elements_map()); @@ -1677,7 +1684,6 @@ bool Genesis::InstallNatives() { global_context()->set_regexp_result_map(*initial_map); } - #ifdef DEBUG builtins->Verify(); #endif @@ -2056,7 +2062,7 @@ void Genesis::TransferNamedProperties(Handle<JSObject> from, break; } case MAP_TRANSITION: - case EXTERNAL_ARRAY_TRANSITION: + case ELEMENTS_TRANSITION: case CONSTANT_TRANSITION: case NULL_DESCRIPTOR: // Ignore non-properties. diff --git a/src/builtins.cc b/src/builtins.cc index d403a951..e6a0699f 100644 --- a/src/builtins.cc +++ b/src/builtins.cc @@ -1583,7 +1583,6 @@ void Builtins::InitBuiltinFunctionTable() { 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 f9a5a13b..31090d3a 100644 --- a/src/builtins.h +++ b/src/builtins.h @@ -238,6 +238,8 @@ enum BuiltinExtraArguments { 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/cached-powers.h b/src/cached-powers.h index 2ae56196..88df2226 100644 --- a/src/cached-powers.h +++ b/src/cached-powers.h @@ -35,7 +35,6 @@ namespace internal { class PowersOfTenCache { public: - // Not all powers of ten are cached. The decimal exponent of two neighboring // cached numbers will differ by kDecimalExponentDistance. static const int kDecimalExponentDistance; diff --git a/src/circular-queue-inl.h b/src/circular-queue-inl.h index 349f2229..373bf609 100644 --- a/src/circular-queue-inl.h +++ b/src/circular-queue-inl.h @@ -1,4 +1,4 @@ -// Copyright 2010 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: @@ -25,8 +25,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#ifndef V8_CIRCULAR_BUFFER_INL_H_ -#define V8_CIRCULAR_BUFFER_INL_H_ +#ifndef V8_CIRCULAR_QUEUE_INL_H_ +#define V8_CIRCULAR_QUEUE_INL_H_ #include "circular-queue.h" @@ -50,4 +50,4 @@ void SamplingCircularQueue::WrapPositionIfNeeded( } } // namespace v8::internal -#endif // V8_CIRCULAR_BUFFER_INL_H_ +#endif // V8_CIRCULAR_QUEUE_INL_H_ diff --git a/src/code-stubs.cc b/src/code-stubs.cc index 5535d171..00da4cba 100644 --- a/src/code-stubs.cc +++ b/src/code-stubs.cc @@ -61,7 +61,7 @@ void CodeStub::GenerateCode(MacroAssembler* masm) { } -SmartPointer<const char> CodeStub::GetName() { +SmartArrayPointer<const char> CodeStub::GetName() { char buffer[100]; NoAllocationStringAllocator allocator(buffer, static_cast<unsigned>(sizeof(buffer))); @@ -75,7 +75,7 @@ void CodeStub::RecordCodeGeneration(Code* code, MacroAssembler* masm) { code->set_major_key(MajorKey()); Isolate* isolate = masm->isolate(); - SmartPointer<const char> name = GetName(); + SmartArrayPointer<const char> name = GetName(); PROFILE(isolate, CodeCreateEvent(Logger::STUB_TAG, code, *name)); GDBJIT(AddCode(GDBJITInterface::STUB, *name, code)); Counters* counters = isolate->counters(); @@ -114,7 +114,6 @@ Handle<Code> CodeStub::GetCode() { // 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 @@ MaybeObject* CodeStub::TryGetCode() { // 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 = @@ -246,27 +244,27 @@ void InstanceofStub::PrintName(StringStream* stream) { void KeyedLoadElementStub::Generate(MacroAssembler* masm) { switch (elements_kind_) { - case JSObject::FAST_ELEMENTS: + case FAST_ELEMENTS: KeyedLoadStubCompiler::GenerateLoadFastElement(masm); break; - case JSObject::FAST_DOUBLE_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: KeyedLoadStubCompiler::GenerateLoadFastDoubleElement(masm); break; - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: KeyedLoadStubCompiler::GenerateLoadExternalArray(masm, elements_kind_); break; - case JSObject::DICTIONARY_ELEMENTS: + case DICTIONARY_ELEMENTS: KeyedLoadStubCompiler::GenerateLoadDictionaryElement(masm); break; - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -275,28 +273,28 @@ void KeyedLoadElementStub::Generate(MacroAssembler* masm) { void KeyedStoreElementStub::Generate(MacroAssembler* masm) { switch (elements_kind_) { - case JSObject::FAST_ELEMENTS: + case FAST_ELEMENTS: KeyedStoreStubCompiler::GenerateStoreFastElement(masm, is_js_array_); break; - case JSObject::FAST_DOUBLE_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: KeyedStoreStubCompiler::GenerateStoreFastDoubleElement(masm, is_js_array_); break; - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: KeyedStoreStubCompiler::GenerateStoreExternalArray(masm, elements_kind_); break; - case JSObject::DICTIONARY_ELEMENTS: + case DICTIONARY_ELEMENTS: KeyedStoreStubCompiler::GenerateStoreDictionaryElement(masm); break; - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -316,17 +314,12 @@ void ArgumentsAccessStub::PrintName(StringStream* stream) { 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 89e99a8d..64c89b93 100644 --- a/src/code-stubs.h +++ b/src/code-stubs.h @@ -168,10 +168,6 @@ class CodeStub BASE_EMBEDDED { 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(); @@ -181,7 +177,7 @@ class CodeStub BASE_EMBEDDED { } // Returns a name for logging/debugging purposes. - SmartPointer<const char> GetName(); + SmartArrayPointer<const char> GetName(); virtual void PrintName(StringStream* stream) { stream->Add("%s", MajorName(MajorKey(), false)); } @@ -646,8 +642,8 @@ class RegExpConstructResultStub: public CodeStub { 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 @@ class CallFunctionStub: public CodeStub { 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; } @@ -860,7 +850,7 @@ class AllowStubCallsScope { class KeyedLoadElementStub : public CodeStub { public: - explicit KeyedLoadElementStub(JSObject::ElementsKind elements_kind) + explicit KeyedLoadElementStub(ElementsKind elements_kind) : elements_kind_(elements_kind) { } @@ -870,7 +860,7 @@ class KeyedLoadElementStub : public CodeStub { void Generate(MacroAssembler* masm); private: - JSObject::ElementsKind elements_kind_; + ElementsKind elements_kind_; DISALLOW_COPY_AND_ASSIGN(KeyedLoadElementStub); }; @@ -879,20 +869,20 @@ class KeyedLoadElementStub : public CodeStub { class KeyedStoreElementStub : public CodeStub { public: KeyedStoreElementStub(bool is_js_array, - JSObject::ElementsKind elements_kind) + ElementsKind elements_kind) : is_js_array_(is_js_array), elements_kind_(elements_kind) { } Major MajorKey() { return KeyedStoreElement; } int MinorKey() { - return (is_js_array_ ? 0 : JSObject::kElementsKindCount) + elements_kind_; + return (is_js_array_ ? 0 : kElementsKindCount) + elements_kind_; } void Generate(MacroAssembler* masm); private: bool is_js_array_; - JSObject::ElementsKind elements_kind_; + ElementsKind elements_kind_; DISALLOW_COPY_AND_ASSIGN(KeyedStoreElementStub); }; diff --git a/src/compilation-cache.h b/src/compilation-cache.h index 1fcf7531..4339d226 100644 --- a/src/compilation-cache.h +++ b/src/compilation-cache.h @@ -242,6 +242,7 @@ class CompilationCache { // cache during debugging to make sure new scripts are always compiled. void Enable(); void Disable(); + private: explicit CompilationCache(Isolate* isolate); ~CompilationCache(); diff --git a/src/compiler.cc b/src/compiler.cc index b33c374d..5e1c4a97 100644 --- a/src/compiler.cc +++ b/src/compiler.cc @@ -41,6 +41,7 @@ #include "parser.h" #include "rewriter.h" #include "runtime-profiler.h" +#include "scanner-character-streams.h" #include "scopeinfo.h" #include "scopes.h" #include "vm-state-inl.h" diff --git a/src/compiler.h b/src/compiler.h index 181446b2..69ab27d9 100644 --- a/src/compiler.h +++ b/src/compiler.h @@ -49,11 +49,11 @@ class CompilationInfo BASE_EMBEDDED { ASSERT(Isolate::Current() == isolate_); return isolate_; } - bool is_lazy() const { return (flags_ & IsLazy::mask()) != 0; } - bool is_eval() const { return (flags_ & IsEval::mask()) != 0; } - bool is_global() const { return (flags_ & IsGlobal::mask()) != 0; } - bool is_strict_mode() const { return (flags_ & IsStrictMode::mask()) != 0; } - bool is_in_loop() const { return (flags_ & IsInLoop::mask()) != 0; } + bool is_lazy() const { return IsLazy::decode(flags_); } + bool is_eval() const { return IsEval::decode(flags_); } + bool is_global() const { return IsGlobal::decode(flags_); } + bool is_strict_mode() const { return IsStrictMode::decode(flags_); } + bool is_in_loop() const { return IsInLoop::decode(flags_); } FunctionLiteral* function() const { return function_; } Scope* scope() const { return scope_; } Handle<Code> code() const { return code_; } diff --git a/src/conversions-inl.h b/src/conversions-inl.h index 1a20645a..41cf0d54 100644 --- a/src/conversions-inl.h +++ b/src/conversions-inl.h @@ -32,19 +32,22 @@ #include <math.h> #include <float.h> // Required for DBL_MAX and on Win32 for finite() #include <stdarg.h> +#include "globals.h" // Required for V8_INFINITY // ---------------------------------------------------------------------------- // Extra POSIX/ANSI functions for Win32/MSVC. #include "conversions.h" -#include "strtod.h" +#include "double.h" #include "platform.h" +#include "scanner.h" +#include "strtod.h" namespace v8 { namespace internal { static inline double JunkStringValue() { - return BitCast<double, uint64_t>(kQuietNaNMask); + return std::numeric_limits<double>::quiet_NaN(); } @@ -87,12 +90,15 @@ static inline double DoubleToInteger(double x) { int32_t DoubleToInt32(double x) { int32_t i = FastD2I(x); if (FastI2D(i) == x) return i; - static const double two32 = 4294967296.0; - static const double two31 = 2147483648.0; - if (!isfinite(x) || x == 0) return 0; - if (x < 0 || x >= two32) x = modulo(x, two32); - x = (x >= 0) ? floor(x) : ceil(x) + two32; - return (int32_t) ((x >= two31) ? x - two32 : x); + Double d(x); + int exponent = d.Exponent(); + if (exponent < 0) { + if (exponent <= -Double::kSignificandSize) return 0; + return d.Sign() * static_cast<int32_t>(d.Significand() >> -exponent); + } else { + if (exponent > 31) return 0; + return d.Sign() * static_cast<int32_t>(d.Significand() << exponent); + } } diff --git a/src/conversions.cc b/src/conversions.cc index c34fe519..5bfddd04 100644 --- a/src/conversions.cc +++ b/src/conversions.cc @@ -26,11 +26,11 @@ // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #include <stdarg.h> +#include <math.h> #include <limits.h> #include "conversions-inl.h" #include "dtoa.h" -#include "scanner-base.h" #include "strtod.h" #include "utils.h" @@ -38,7 +38,6 @@ namespace v8 { namespace internal { - double StringToDouble(UnicodeCache* unicode_cache, const char* str, int flags, double empty_string_val) { const char* end = str + StrLength(str); @@ -390,7 +389,7 @@ char* DoubleToRadixCString(double value, int radix) { int integer_pos = kBufferSize - 2; do { integer_buffer[integer_pos--] = - chars[static_cast<int>(modulo(integer_part, radix))]; + chars[static_cast<int>(fmod(integer_part, radix))]; integer_part /= radix; } while (integer_part >= 1.0); // Sanity check. diff --git a/src/conversions.h b/src/conversions.h index 9c0b8f35..e51ad650 100644 --- a/src/conversions.h +++ b/src/conversions.h @@ -28,12 +28,15 @@ #ifndef V8_CONVERSIONS_H_ #define V8_CONVERSIONS_H_ -#include "scanner-base.h" +#include <limits> + #include "utils.h" namespace v8 { namespace internal { +class UnicodeCache; + // Maximum number of significant digits in decimal representation. // The longest possible double in decimal representation is // (2^53 - 1) * 2 ^ -1074 that is (2 ^ 53 - 1) * 5 ^ 1074 / 10 ^ 1074 @@ -44,14 +47,14 @@ namespace internal { const int kMaxSignificantDigits = 772; -static bool isDigit(int x, int radix) { +static inline bool isDigit(int x, int radix) { return (x >= '0' && x <= '9' && x < '0' + radix) || (radix > 10 && x >= 'a' && x < 'a' + radix - 10) || (radix > 10 && x >= 'A' && x < 'A' + radix - 10); } -static double SignedZero(bool negative) { +static inline double SignedZero(bool negative) { return negative ? -0.0 : 0.0; } @@ -124,6 +127,8 @@ double StringToDouble(UnicodeCache* unicode_cache, int flags, double empty_string_val = 0); +const int kDoubleToCStringMinBufferSize = 100; + // Converts a double to a string value according to ECMA-262 9.8.1. // The buffer should be large enough for any floating point number. // 100 characters is enough. diff --git a/src/cpu-profiler-inl.h b/src/cpu-profiler-inl.h index 938b6322..4982197c 100644 --- a/src/cpu-profiler-inl.h +++ b/src/cpu-profiler-inl.h @@ -51,11 +51,6 @@ void CodeMoveEventRecord::UpdateCodeMap(CodeMap* code_map) { } -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 8b3333ea..d74c034a 100644 --- a/src/cpu-profiler.cc +++ b/src/cpu-profiler.cc @@ -137,16 +137,6 @@ void ProfilerEventsProcessor::CodeMoveEvent(Address from, Address to) { } -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::CodeMoveEvent(Address from, Address to) { 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 4175e8f6..a71c0e0a 100644 --- a/src/cpu-profiler.h +++ b/src/cpu-profiler.h @@ -48,7 +48,6 @@ class TokenEnumerator; #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 CodeMoveEventRecord : public CodeEventRecord { }; -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-debug.cc b/src/d8-debug.cc index 06622057..adefba73 100644 --- a/src/d8-debug.cc +++ b/src/d8-debug.cc @@ -221,14 +221,14 @@ void RemoteDebugger::Run() { } -void RemoteDebugger::MessageReceived(i::SmartPointer<char> message) { +void RemoteDebugger::MessageReceived(i::SmartArrayPointer<char> message) { RemoteDebuggerEvent* event = new RemoteDebuggerEvent(RemoteDebuggerEvent::kMessage, message); AddEvent(event); } -void RemoteDebugger::KeyboardCommand(i::SmartPointer<char> command) { +void RemoteDebugger::KeyboardCommand(i::SmartArrayPointer<char> command) { RemoteDebuggerEvent* event = new RemoteDebuggerEvent(RemoteDebuggerEvent::kKeyboard, command); AddEvent(event); @@ -238,7 +238,7 @@ void RemoteDebugger::KeyboardCommand(i::SmartPointer<char> command) { void RemoteDebugger::ConnectionClosed() { RemoteDebuggerEvent* event = new RemoteDebuggerEvent(RemoteDebuggerEvent::kDisconnect, - i::SmartPointer<char>()); + i::SmartArrayPointer<char>()); AddEvent(event); } @@ -330,14 +330,14 @@ void RemoteDebugger::HandleKeyboardCommand(char* command) { void ReceiverThread::Run() { // Receive the connect message (with empty body). - i::SmartPointer<char> message = - i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn()); + i::SmartArrayPointer<char> message = + i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn()); ASSERT(*message == NULL); while (true) { // Receive a message. - i::SmartPointer<char> message = - i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn()); + i::SmartArrayPointer<char> message = + i::DebuggerAgentUtil::ReceiveMessage(remote_debugger_->conn()); if (*message == NULL) { remote_debugger_->ConnectionClosed(); return; @@ -361,7 +361,7 @@ void KeyboardThread::Run() { // Pass the keyboard command to the main thread. remote_debugger_->KeyboardCommand( - i::SmartPointer<char>(i::StrDup(command))); + i::SmartArrayPointer<char>(i::StrDup(command))); } } diff --git a/src/d8-debug.h b/src/d8-debug.h index 4e33e6f4..aeff3c12 100644 --- a/src/d8-debug.h +++ b/src/d8-debug.h @@ -61,8 +61,8 @@ class RemoteDebugger { void Run(); // Handle events from the subordinate threads. - void MessageReceived(i::SmartPointer<char> message); - void KeyboardCommand(i::SmartPointer<char> command); + void MessageReceived(i::SmartArrayPointer<char> message); + void KeyboardCommand(i::SmartArrayPointer<char> command); void ConnectionClosed(); private: @@ -127,7 +127,7 @@ class KeyboardThread: public i::Thread { // Events processed by the main deubgger thread. class RemoteDebuggerEvent { public: - RemoteDebuggerEvent(int type, i::SmartPointer<char> data) + RemoteDebuggerEvent(int type, i::SmartArrayPointer<char> data) : type_(type), data_(data), next_(NULL) { ASSERT(type == kMessage || type == kKeyboard || type == kDisconnect); } @@ -144,7 +144,7 @@ class RemoteDebuggerEvent { RemoteDebuggerEvent* next() { return next_; } int type_; - i::SmartPointer<char> data_; + i::SmartArrayPointer<char> data_; RemoteDebuggerEvent* next_; friend class RemoteDebugger; diff --git a/src/d8-posix.cc b/src/d8-posix.cc index 658fd4ff..289c3b0a 100644 --- a/src/d8-posix.cc +++ b/src/d8-posix.cc @@ -231,6 +231,7 @@ class ExecArgs { static const unsigned kMaxArgs = 1000; char** arg_array() { return exec_args_; } char* arg0() { return exec_args_[0]; } + private: char* exec_args_[kMaxArgs + 1]; }; diff --git a/src/d8-readline.cc b/src/d8-readline.cc index 08395e53..71be9331 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: @@ -49,7 +49,7 @@ namespace v8 { class ReadLineEditor: public LineEditor { public: ReadLineEditor() : LineEditor(LineEditor::READLINE, "readline") { } - virtual i::SmartPointer<char> Prompt(const char* prompt); + virtual i::SmartArrayPointer<char> Prompt(const char* prompt); virtual bool Open(); virtual bool Close(); virtual void AddHistory(const char* str); @@ -72,6 +72,7 @@ bool ReadLineEditor::Open() { rl_completer_word_break_characters = kWordBreakCharacters; rl_bind_key('\t', rl_complete); using_history(); + stifle_history(Shell::kMaxHistoryEntries); return read_history(Shell::kHistoryFileName) == 0; } @@ -81,13 +82,25 @@ bool ReadLineEditor::Close() { } -i::SmartPointer<char> ReadLineEditor::Prompt(const char* prompt) { +i::SmartArrayPointer<char> ReadLineEditor::Prompt(const char* prompt) { char* result = readline(prompt); - return i::SmartPointer<char>(result); + return i::SmartArrayPointer<char>(result); } 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); } @@ -105,7 +118,7 @@ char* ReadLineEditor::CompletionGenerator(const char* text, int state) { static unsigned current_index; static Persistent<Array> current_completions; if (state == 0) { - i::SmartPointer<char> full_text(i::StrNDup(rl_line_buffer, rl_point)); + i::SmartArrayPointer<char> full_text(i::StrNDup(rl_line_buffer, rl_point)); HandleScope scope; Handle<Array> completions = Shell::GetCompletions(String::New(text), String::New(*full_text)); @@ -70,6 +70,7 @@ namespace v8 { #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) @@ -95,19 +96,19 @@ LineEditor* LineEditor::Get() { class DumbLineEditor: public LineEditor { public: DumbLineEditor() : LineEditor(LineEditor::DUMB, "dumb") { } - virtual i::SmartPointer<char> Prompt(const char* prompt); + virtual i::SmartArrayPointer<char> Prompt(const char* prompt); }; static DumbLineEditor dumb_line_editor; -i::SmartPointer<char> DumbLineEditor::Prompt(const char* prompt) { +i::SmartArrayPointer<char> DumbLineEditor::Prompt(const char* prompt) { static const int kBufferSize = 256; char buffer[kBufferSize]; printf("%s", prompt); char* str = fgets(buffer, kBufferSize, stdin); - return i::SmartPointer<char>(str ? i::StrDup(str) : str); + return i::SmartArrayPointer<char>(str ? i::StrDup(str) : str); } @@ -117,6 +118,7 @@ CounterCollection Shell::local_counters_; 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_; @@ -176,8 +178,8 @@ bool Shell::ExecuteString(Handle<String> source, // If all went well and the result wasn't undefined then print // the returned value. v8::String::Utf8Value str(result); - const char* cstr = ToCString(str); - printf("%s\n", cstr); + fwrite(*str, sizeof(**str), str.length(), stdout); + printf("\n"); } return true; } @@ -203,13 +205,25 @@ Handle<Value> Shell::Write(const Arguments& args) { 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(); } +Handle<Value> Shell::EnableProfiler(const Arguments& args) { + V8::ResumeProfiler(); + return Undefined(); +} + + +Handle<Value> Shell::DisableProfiler(const Arguments& args) { + V8::PauseProfiler(); + return Undefined(); +} + + Handle<Value> Shell::Read(const Arguments& args) { String::Utf8Value file(args[0]); if (*file == NULL) { @@ -259,7 +273,7 @@ Handle<Value> Shell::Load(const Arguments& args) { if (source.IsEmpty()) { return ThrowException(String::New("Error loading file")); } - if (!ExecuteString(source, String::New(*file), false, false)) { + if (!ExecuteString(source, String::New(*file), false, true)) { return ThrowException(String::New("Error executing file")); } } @@ -283,18 +297,20 @@ Handle<Value> Shell::CreateExternalArray(const Arguments& args, size_t length = 0; if (args[0]->IsUint32()) { length = args[0]->Uint32Value(); - } else if (args[0]->IsNumber()) { - double raw_length = args[0]->NumberValue(); + } else { + Local<Number> number = args[0]->ToNumber(); + if (number.IsEmpty() || !number->IsNumber()) { + return ThrowException(String::New("Array length must be a number.")); + } + int32_t raw_length = number->ToInt32()->Int32Value(); if (raw_length < 0) { return ThrowException(String::New("Array length must not be negative.")); } - if (raw_length > kMaxLength) { + if (raw_length > static_cast<int32_t>(kMaxLength)) { return ThrowException( String::New("Array length exceeds maximum length.")); } length = static_cast<size_t>(raw_length); - } else { - return ThrowException(String::New("Array length must be a number.")); } if (length > static_cast<size_t>(kMaxLength)) { return ThrowException(String::New("Array length exceeds maximum length.")); @@ -427,6 +443,7 @@ void Shell::ReportException(v8::TryCatch* try_catch) { printf("%s\n", stack_trace_string); } } + printf("\n"); } @@ -506,7 +523,7 @@ void Shell::MapCounters(const char* name) { 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); @@ -656,6 +673,10 @@ Handle<ObjectTemplate> Shell::CreateGlobalTemplate() { global_template->Set(String::New("load"), FunctionTemplate::New(Load)); global_template->Set(String::New("quit"), FunctionTemplate::New(Quit)); global_template->Set(String::New("version"), FunctionTemplate::New(Version)); + global_template->Set(String::New("enableProfiler"), + FunctionTemplate::New(EnableProfiler)); + global_template->Set(String::New("disableProfiler"), + FunctionTemplate::New(DisableProfiler)); // Bind the handlers for external arrays. global_template->Set(String::New("Int8Array"), @@ -683,7 +704,7 @@ Handle<ObjectTemplate> Shell::CreateGlobalTemplate() { global_template->Set(String::New("lol_is_enabled"), False()); #endif -#ifndef V8_SHARED +#if !defined(V8_SHARED) && !defined(_WIN32) && !defined(_WIN64) Handle<ObjectTemplate> os_templ = ObjectTemplate::New(); AddOSMethods(os_templ); global_template->Set(String::New("os"), os_templ); @@ -699,7 +720,7 @@ void Shell::Initialize() { 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 @@ -761,8 +782,18 @@ Persistent<Context> Shell::CreateEvaluationContext() { } +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"); @@ -864,22 +895,22 @@ Handle<String> Shell::ReadFile(const char* name) { void Shell::RunShell() { Locker locker; Context::Scope context_scope(evaluation_context_); - HandleScope handle_scope; + 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::SmartPointer<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; @@ -887,6 +918,7 @@ void Shell::RunShell() { char buffer[kBufferSize]; printf("%s", Shell::kPrompt); if (fgets(buffer, kBufferSize, stdin) == NULL) break; + HandleScope inner_scope; ExecuteString(String::New(buffer), name, true, true); } #endif // V8_SHARED @@ -897,18 +929,24 @@ void Shell::RunShell() { #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); @@ -921,23 +959,24 @@ void ShellThread::Run() { // 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); @@ -950,12 +989,15 @@ void ShellThread::Run() { #endif // V8_SHARED -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); +SourceGroup::~SourceGroup() { +#ifndef V8_SHARED + delete next_semaphore_; + next_semaphore_ = NULL; + delete done_semaphore_; + done_semaphore_ = NULL; + delete thread_; + thread_ = NULL; +#endif // V8_SHARED } @@ -968,8 +1010,7 @@ void SourceGroup::Execute() { 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] == '-') { @@ -981,12 +1022,10 @@ void SourceGroup::Execute() { 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); } } } @@ -1050,7 +1089,6 @@ void SourceGroup::WaitForThread() { if (thread_ == NULL) return; if (Shell::options.last_run) { thread_->Join(); - thread_ = NULL; } else { done_semaphore_->Wait(); } @@ -1123,14 +1161,18 @@ bool Shell::SetOptions(int argc, char* argv[]) { 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; @@ -1140,6 +1182,8 @@ bool Shell::SetOptions(int argc, char* argv[]) { #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) { @@ -1148,25 +1192,21 @@ bool Shell::SetOptions(int argc, char* argv[]) { 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); @@ -1189,14 +1229,22 @@ bool Shell::SetOptions(int argc, char* argv[]) { 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(); } @@ -1205,29 +1253,20 @@ int Shell::RunMain(int argc, char* argv[]) { Locker lock; HandleScope scope; Persistent<Context> context = CreateEvaluationContext(); - if (options.last_run) { - // Keep using the same context in the interactive shell. - evaluation_context_ = context; -#ifndef V8_SHARED - // If the interactive debugger is enabled make sure to activate - // it before running the files passed on the command line. - if (i::FLAG_debugger) { - InstallUtilityScript(); - } -#endif // V8_SHARED - } { Context::Scope cscope(context); options.isolate_sources[0].Execute(); } - if (!options.last_run) { + if (options.last_run) { + // Keep using the same context in the interactive shell + evaluation_context_ = context; + } else { context.Dispose(); } #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); } @@ -1239,12 +1278,16 @@ int Shell::RunMain(int argc, char* argv[]) { 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; } @@ -1289,9 +1332,7 @@ int Shell::Main(int argc, char* argv[]) { || !options.script_executed ) && !options.test_shell ) { #ifndef V8_SHARED - if (!i::FLAG_debugger) { - InstallUtilityScript(); - } + InstallUtilityScript(); #endif // V8_SHARED RunShell(); } @@ -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: @@ -28,11 +28,11 @@ #ifndef V8_D8_H_ #define V8_D8_H_ - #ifndef V8_SHARED -#include "v8.h" #include "allocation.h" #include "hashmap.h" +#include "smart-array-pointer.h" +#include "v8.h" #else #include "../include/v8.h" #endif // V8_SHARED @@ -116,6 +116,29 @@ class CounterMap { #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() : @@ -126,7 +149,9 @@ class SourceGroup { #endif // V8_SHARED argv_(NULL), begin_offset_(0), - end_offset_(0) { } + end_offset_(0) {} + + ~SourceGroup(); void Begin(char** argv, int offset) { argv_ = const_cast<const char**>(argv); @@ -178,6 +203,7 @@ class ShellOptions { #ifndef V8_SHARED use_preemption(true), preemption_interval(10), + num_parallel_files(0), parallel_files(NULL), #endif // V8_SHARED script_executed(false), @@ -189,10 +215,18 @@ class ShellOptions { 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; @@ -209,6 +243,7 @@ class Shell { #else class Shell : public i::AllStatic { #endif // V8_SHARED + public: static bool ExecuteString(Handle<String> source, Handle<Value> name, @@ -220,6 +255,7 @@ class Shell : public i::AllStatic { 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, @@ -248,6 +284,8 @@ class Shell : public i::AllStatic { static Handle<Value> Yield(const Arguments& args); static Handle<Value> Quit(const Arguments& args); static Handle<Value> Version(const Arguments& args); + static Handle<Value> EnableProfiler(const Arguments& args); + static Handle<Value> DisableProfiler(const Arguments& args); static Handle<Value> Read(const Arguments& args); static Handle<Value> ReadLine(const Arguments& args); static Handle<Value> Load(const Arguments& args); @@ -298,6 +336,8 @@ class Shell : public i::AllStatic { 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; @@ -328,29 +368,6 @@ class Shell : public i::AllStatic { }; -#ifndef V8_SHARED -class LineEditor { - public: - enum Type { DUMB = 0, READLINE = 1 }; - LineEditor(Type type, const char* name); - virtual ~LineEditor() { } - - virtual i::SmartPointer<char> Prompt(const char* prompt) = 0; - virtual bool Open() { return true; } - virtual bool Close() { return true; } - virtual void AddHistory(const char* str) { } - - const char* name() { return name_; } - static LineEditor* Get(); - private: - Type type_; - const char* name_; - LineEditor* next_; - static LineEditor* first_; -}; -#endif // V8_SHARED - - } // namespace v8 @@ -1786,7 +1786,7 @@ function decodeLolInfoResponse(body) { function decodeLolListResponse(body, title) { - + var result; var total_count = body.count; var total_size = body.size; diff --git a/src/date.js b/src/date.js index 79b846d4..ccefce57 100644 --- a/src/date.js +++ b/src/date.js @@ -1048,18 +1048,19 @@ function ResetDateCache() { // ------------------------------------------------------------------- -function SetupDate() { - // Setup non-enumerable properties of the Date object itself. +function SetUpDate() { + %CheckIsBootstrapping(); + // Set up non-enumerable properties of the Date object itself. InstallFunctions($Date, DONT_ENUM, $Array( "UTC", DateUTC, "parse", DateParse, "now", DateNow )); - // Setup non-enumerable constructor property of the Date prototype object. + // Set up non-enumerable constructor property of the Date prototype object. %SetProperty($Date.prototype, "constructor", $Date, DONT_ENUM); - // Setup non-enumerable functions of the Date prototype object and + // Set up non-enumerable functions of the Date prototype object and // set their names. InstallFunctionsOnHiddenPrototype($Date.prototype, DONT_ENUM, $Array( "toString", DateToString, @@ -1111,4 +1112,4 @@ function SetupDate() { )); } -SetupDate(); +SetUpDate(); diff --git a/src/dateparser.h b/src/dateparser.h index 4bd320e9..27584ce3 100644 --- a/src/dateparser.h +++ b/src/dateparser.h @@ -30,14 +30,12 @@ #include "allocation.h" #include "char-predicates-inl.h" -#include "scanner-base.h" namespace v8 { namespace internal { class DateParser : public AllStatic { public: - // Parse the string as a date. If parsing succeeds, return true after // filling out the output array as follows (all integers are Smis): // [0]: year @@ -235,6 +233,7 @@ class DateParser : public AllStatic { static DateToken Invalid() { return DateToken(kInvalidTokenTag, 0, -1); } + private: enum TagType { kInvalidTokenTag = -6, @@ -276,6 +275,7 @@ class DateParser : public AllStatic { } return false; } + private: DateToken Scan(); @@ -352,6 +352,7 @@ class DateParser : public AllStatic { static bool IsMinute(int x) { return Between(x, 0, 59); } static bool IsHour(int x) { return Between(x, 0, 23); } static bool IsSecond(int x) { return Between(x, 0, 59); } + private: static bool IsHour12(int x) { return Between(x, 0, 12); } static bool IsMillisecond(int x) { return Between(x, 0, 999); } diff --git a/src/debug-agent.cc b/src/debug-agent.cc index 520bc629..591d0b3e 100644 --- a/src/debug-agent.cc +++ b/src/debug-agent.cc @@ -169,7 +169,8 @@ void DebuggerAgentSession::Run() { while (true) { // Read data from the debugger front end. - SmartPointer<char> message = DebuggerAgentUtil::ReceiveMessage(client_); + SmartArrayPointer<char> message = + DebuggerAgentUtil::ReceiveMessage(client_); const char* msg = *message; bool is_closing_session = (msg == NULL); @@ -232,7 +233,7 @@ const int DebuggerAgentUtil::kContentLengthSize = StrLength(kContentLength); -SmartPointer<char> DebuggerAgentUtil::ReceiveMessage(const Socket* conn) { +SmartArrayPointer<char> DebuggerAgentUtil::ReceiveMessage(const Socket* conn) { int received; // Read header. @@ -250,7 +251,7 @@ SmartPointer<char> DebuggerAgentUtil::ReceiveMessage(const Socket* conn) { received = conn->Receive(&c, 1); if (received <= 0) { PrintF("Error %d\n", Socket::LastError()); - return SmartPointer<char>(); + return SmartArrayPointer<char>(); } // Add character to header buffer. @@ -287,12 +288,12 @@ SmartPointer<char> DebuggerAgentUtil::ReceiveMessage(const Socket* conn) { if (strcmp(key, kContentLength) == 0) { // Get the content length value if present and within a sensible range. if (value == NULL || strlen(value) > 7) { - return SmartPointer<char>(); + return SmartArrayPointer<char>(); } for (int i = 0; value[i] != '\0'; i++) { // Bail out if illegal data. if (value[i] < '0' || value[i] > '9') { - return SmartPointer<char>(); + return SmartArrayPointer<char>(); } content_length = 10 * content_length + (value[i] - '0'); } @@ -304,7 +305,7 @@ SmartPointer<char> DebuggerAgentUtil::ReceiveMessage(const Socket* conn) { // Return now if no body. if (content_length == 0) { - return SmartPointer<char>(); + return SmartArrayPointer<char>(); } // Read body. @@ -312,11 +313,11 @@ SmartPointer<char> DebuggerAgentUtil::ReceiveMessage(const Socket* conn) { received = ReceiveAll(conn, buffer, content_length); if (received < content_length) { PrintF("Error %d\n", Socket::LastError()); - return SmartPointer<char>(); + return SmartArrayPointer<char>(); } buffer[content_length] = '\0'; - return SmartPointer<char>(buffer); + return SmartArrayPointer<char>(buffer); } diff --git a/src/debug-agent.h b/src/debug-agent.h index e1678719..a07fb0f4 100644 --- a/src/debug-agent.h +++ b/src/debug-agent.h @@ -72,7 +72,7 @@ class DebuggerAgent: public Thread { void OnSessionClosed(DebuggerAgentSession* session); Isolate* isolate_; - SmartPointer<const char> name_; // Name of the embedding application. + SmartArrayPointer<const char> name_; // Name of the embedding application. int port_; // Port to use for the agent. Socket* server_; // Server socket for listen/accept. bool terminate_; // Termination flag. @@ -117,7 +117,7 @@ class DebuggerAgentUtil { static const char* const kContentLength; static const int kContentLengthSize; - static SmartPointer<char> ReceiveMessage(const Socket* conn); + static SmartArrayPointer<char> ReceiveMessage(const Socket* conn); static bool SendConnectMessage(const Socket* conn, const char* embedding_host); static bool SendMessage(const Socket* conn, const Vector<uint16_t> message); diff --git a/src/debug.cc b/src/debug.cc index 2d58ce1f..20cd8027 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 @@ void Debug::ThreadInit() { 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 @@ Object* Debug::Break(Arguments args) { // 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 @@ void Debug::SetBreakPoint(Handle<SharedFunctionInfo> shared, int* source_position) { HandleScope scope(isolate_); + PrepareForBreakPoints(); + if (!EnsureDebugInfo(shared)) { // Return if retrieving debug info failed. return; @@ -1178,6 +1217,7 @@ void Debug::ClearAllBreakPoints() { 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 @@ bool Debug::IsBreakOnException(ExceptionBreakType type) { 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 @@ void Debug::PrepareStep(StepAction step_action, int step_count) { // 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,20 +1726,26 @@ void Debug::ClearStepNext() { } +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(); + } +} + + // 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; + 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; - // 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(); - } - // Create the debug info object. Handle<DebugInfo> debug_info = FACTORY->NewDebugInfo(shared); @@ -1739,6 +1795,8 @@ void Debug::RemoveDebugInfo(Handle<DebugInfo> debug_info) { 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 +1887,8 @@ bool Debug::IsBreakAtReturn(JavaScriptFrame* frame) { 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 c614844a..a098040c 100644 --- a/src/debug.h +++ b/src/debug.h @@ -247,6 +247,8 @@ class Debug { 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 @@ class Debug { // 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_; @@ -1026,6 +1031,7 @@ class Debug_Address { return NULL; } } + private: Debug::AddressId id_; }; diff --git a/src/deoptimizer.cc b/src/deoptimizer.cc index 94b2ff53..5feb73d7 100644 --- a/src/deoptimizer.cc +++ b/src/deoptimizer.cc @@ -613,13 +613,11 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, intptr_t input_value = input_->GetRegister(input_reg); if (FLAG_trace_deopt) { PrintF( - " 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" V8PRIxPTR " ; %s ", + " 0x%08" V8PRIxPTR ": [top + %d] <- 0x%08" V8PRIxPTR " ; %s\n", output_[frame_index]->GetTop() + output_offset, output_offset, input_value, converter.NameOfCPURegister(input_reg)); - reinterpret_cast<Object*>(input_value)->ShortPrint(); - PrintF("\n"); } output_[frame_index]->SetFrameSlot(output_offset, input_value); return; @@ -677,12 +675,10 @@ void Deoptimizer::DoTranslateCommand(TranslationIterator* iterator, if (FLAG_trace_deopt) { PrintF(" 0x%08" V8PRIxPTR ": ", output_[frame_index]->GetTop() + output_offset); - PrintF("[top + %d] <- 0x%08" V8PRIxPTR " ; [esp + %d] ", + PrintF("[top + %d] <- 0x%08" V8PRIxPTR " ; [esp + %d]\n", output_offset, input_value, input_offset); - reinterpret_cast<Object*>(input_value)->ShortPrint(); - PrintF("\n"); } output_[frame_index]->SetFrameSlot(output_offset, input_value); return; diff --git a/src/disassembler.cc b/src/disassembler.cc index 79076d6a..1e67b4cb 100644 --- a/src/disassembler.cc +++ b/src/disassembler.cc @@ -223,7 +223,7 @@ static int DecodeIt(FILE* f, HeapStringAllocator allocator; StringStream accumulator(&allocator); relocinfo.target_object()->ShortPrint(&accumulator); - SmartPointer<const char> obj_name = accumulator.ToCString(); + SmartArrayPointer<const char> obj_name = accumulator.ToCString(); out.AddFormatted(" ;; object: %s", *obj_name); } else if (rmode == RelocInfo::EXTERNAL_REFERENCE) { const char* reference_name = @@ -247,9 +247,6 @@ static int DecodeIt(FILE* f, 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/dtoa.cc b/src/dtoa.cc index b857a5dc..00233a88 100644 --- a/src/dtoa.cc +++ b/src/dtoa.cc @@ -1,4 +1,4 @@ -// Copyright 2010 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: @@ -27,7 +27,10 @@ #include <math.h> -#include "v8.h" +#include "../include/v8stdint.h" +#include "checks.h" +#include "utils.h" + #include "dtoa.h" #include "bignum-dtoa.h" diff --git a/src/elements.cc b/src/elements.cc index 1afc5dad..e4ecfe8d 100644 --- a/src/elements.cc +++ b/src/elements.cc @@ -401,7 +401,7 @@ class DictionaryElementsAccessor Heap* heap = isolate->heap(); FixedArray* backing_store = FixedArray::cast(obj->elements()); bool is_arguments = - (obj->GetElementsKind() == JSObject::NON_STRICT_ARGUMENTS_ELEMENTS); + (obj->GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS); if (is_arguments) { backing_store = FixedArray::cast(backing_store->get(1)); } @@ -565,28 +565,28 @@ ElementsAccessor* ElementsAccessor::ForArray(FixedArrayBase* array) { switch (array->map()->instance_type()) { case FIXED_ARRAY_TYPE: if (array->IsDictionary()) { - return elements_accessors_[JSObject::DICTIONARY_ELEMENTS]; + return elements_accessors_[DICTIONARY_ELEMENTS]; } else { - return elements_accessors_[JSObject::FAST_ELEMENTS]; + return elements_accessors_[FAST_ELEMENTS]; } case EXTERNAL_BYTE_ARRAY_TYPE: - return elements_accessors_[JSObject::EXTERNAL_BYTE_ELEMENTS]; + return elements_accessors_[EXTERNAL_BYTE_ELEMENTS]; case EXTERNAL_UNSIGNED_BYTE_ARRAY_TYPE: - return elements_accessors_[JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS]; + return elements_accessors_[EXTERNAL_UNSIGNED_BYTE_ELEMENTS]; case EXTERNAL_SHORT_ARRAY_TYPE: - return elements_accessors_[JSObject::EXTERNAL_SHORT_ELEMENTS]; + return elements_accessors_[EXTERNAL_SHORT_ELEMENTS]; case EXTERNAL_UNSIGNED_SHORT_ARRAY_TYPE: - return elements_accessors_[JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS]; + return elements_accessors_[EXTERNAL_UNSIGNED_SHORT_ELEMENTS]; case EXTERNAL_INT_ARRAY_TYPE: - return elements_accessors_[JSObject::EXTERNAL_INT_ELEMENTS]; + return elements_accessors_[EXTERNAL_INT_ELEMENTS]; case EXTERNAL_UNSIGNED_INT_ARRAY_TYPE: - return elements_accessors_[JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS]; + return elements_accessors_[EXTERNAL_UNSIGNED_INT_ELEMENTS]; case EXTERNAL_FLOAT_ARRAY_TYPE: - return elements_accessors_[JSObject::EXTERNAL_FLOAT_ELEMENTS]; + return elements_accessors_[EXTERNAL_FLOAT_ELEMENTS]; case EXTERNAL_DOUBLE_ARRAY_TYPE: - return elements_accessors_[JSObject::EXTERNAL_DOUBLE_ELEMENTS]; + return elements_accessors_[EXTERNAL_DOUBLE_ELEMENTS]; case EXTERNAL_PIXEL_ARRAY_TYPE: - return elements_accessors_[JSObject::EXTERNAL_PIXEL_ELEMENTS]; + return elements_accessors_[EXTERNAL_PIXEL_ELEMENTS]; default: UNREACHABLE(); return NULL; diff --git a/src/elements.h b/src/elements.h index 3eae303e..851c8c3d 100644 --- a/src/elements.h +++ b/src/elements.h @@ -54,8 +54,8 @@ class ElementsAccessor { Object* receiver) = 0; // Returns a shared ElementsAccessor for the specified ElementsKind. - static ElementsAccessor* ForKind(JSObject::ElementsKind elements_kind) { - ASSERT(elements_kind < JSObject::kElementsKindCount); + static ElementsAccessor* ForKind(ElementsKind elements_kind) { + ASSERT(elements_kind < kElementsKindCount); return elements_accessors_[elements_kind]; } diff --git a/src/execution.cc b/src/execution.cc index bdbdca81..f36d4e49 100644 --- a/src/execution.cc +++ b/src/execution.cc @@ -149,12 +149,29 @@ Handle<Object> Execution::Call(Handle<Object> callable, 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 @@ Handle<Object> Execution::GetFunctionDelegate(Handle<Object> object) { // 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 @@ Handle<Object> Execution::TryGetFunctionDelegate(Handle<Object> object, 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 @@ Handle<Object> Execution::GetConstructorDelegate(Handle<Object> object) { // 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 @@ Handle<Object> Execution::TryGetConstructorDelegate( // 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::ToDetailString(Handle<Object> obj, bool* exc) { 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 bb5f8045..5cd7141f 100644 --- a/src/execution.h +++ b/src/execution.h @@ -53,11 +53,16 @@ class Execution : public AllStatic { // *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/extensions/externalize-string-extension.cc b/src/extensions/externalize-string-extension.cc index b3f83fe9..9fbf3298 100644 --- a/src/extensions/externalize-string-extension.cc +++ b/src/extensions/externalize-string-extension.cc @@ -133,9 +133,11 @@ v8::Handle<v8::Value> ExternalizeStringExtension::IsAscii( void ExternalizeStringExtension::Register() { - static ExternalizeStringExtension externalize_extension; + static ExternalizeStringExtension* externalize_extension = NULL; + if (externalize_extension == NULL) + externalize_extension = new ExternalizeStringExtension; static v8::DeclareExtension externalize_extension_declaration( - &externalize_extension); + externalize_extension); } } } // namespace v8::internal diff --git a/src/factory.cc b/src/factory.cc index ee5c37bf..97289266 100644 --- a/src/factory.cc +++ b/src/factory.cc @@ -465,13 +465,13 @@ Handle<Map> Factory::GetSlowElementsMap(Handle<Map> src) { } -Handle<Map> Factory::GetExternalArrayElementsMap( +Handle<Map> Factory::GetElementsTransitionMap( Handle<Map> src, - ExternalArrayType array_type, + ElementsKind elements_kind, bool safe_to_add_transition) { CALL_HEAP_FUNCTION(isolate(), - src->GetExternalArrayElementsMap(array_type, - safe_to_add_transition), + src->GetElementsTransitionMap(elements_kind, + safe_to_add_transition), Map); } @@ -922,10 +922,19 @@ Handle<JSProxy> Factory::NewJSProxy(Handle<Object> handler, } -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 a69b05b3..71ae750b 100644 --- a/src/factory.h +++ b/src/factory.h @@ -219,9 +219,9 @@ class Factory { Handle<Map> GetSlowElementsMap(Handle<Map> map); - Handle<Map> GetExternalArrayElementsMap(Handle<Map> map, - ExternalArrayType array_type, - bool safe_to_add_transition); + Handle<Map> GetElementsTransitionMap(Handle<Map> map, + ElementsKind elements_kind, + bool safe_to_add_transition); Handle<FixedArray> CopyFixedArray(Handle<FixedArray> array); @@ -260,8 +260,9 @@ class Factory { 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/fast-dtoa.cc b/src/fast-dtoa.cc index c7f6aa17..e62bd01f 100644 --- a/src/fast-dtoa.cc +++ b/src/fast-dtoa.cc @@ -1,4 +1,4 @@ -// Copyright 2010 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: @@ -25,7 +25,9 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "v8.h" +#include "../include/v8stdint.h" +#include "checks.h" +#include "utils.h" #include "fast-dtoa.h" diff --git a/src/fixed-dtoa.cc b/src/fixed-dtoa.cc index 8ad88f65..1fd974c3 100644 --- a/src/fixed-dtoa.cc +++ b/src/fixed-dtoa.cc @@ -1,4 +1,4 @@ -// Copyright 2010 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: @@ -27,7 +27,9 @@ #include <math.h> -#include "v8.h" +#include "../include/v8stdint.h" +#include "checks.h" +#include "utils.h" #include "double.h" #include "fixed-dtoa.h" diff --git a/src/flags.cc b/src/flags.cc index c20f5ee0..ab5b57ce 100644 --- a/src/flags.cc +++ b/src/flags.cc @@ -31,7 +31,7 @@ #include "v8.h" #include "platform.h" -#include "smart-pointer.h" +#include "smart-array-pointer.h" #include "string-stream.h" @@ -193,7 +193,7 @@ static const char* Type2String(Flag::FlagType type) { } -static SmartPointer<const char> ToString(Flag* flag) { +static SmartArrayPointer<const char> ToString(Flag* flag) { HeapStringAllocator string_allocator; StringStream buffer(&string_allocator); switch (flag->type()) { @@ -528,7 +528,7 @@ void FlagList::PrintHelp() { printf("Options:\n"); for (size_t i = 0; i < num_flags; ++i) { Flag* f = &flags[i]; - SmartPointer<const char> value = ToString(f); + SmartArrayPointer<const char> value = ToString(f); printf(" --%s (%s)\n type: %s default: %s\n", f->name(), f->comment(), Type2String(f->type()), *value); } diff --git a/src/frames.h b/src/frames.h index 4f94ebc7..fed11c4f 100644 --- a/src/frames.h +++ b/src/frames.h @@ -579,6 +579,7 @@ class ArgumentsAdaptorFrame: public JavaScriptFrame { virtual void Print(StringStream* accumulator, PrintMode mode, int index) const; + protected: explicit ArgumentsAdaptorFrame(StackFrameIterator* iterator) : JavaScriptFrame(iterator) { } diff --git a/src/full-codegen.cc b/src/full-codegen.cc index ca2026bb..80738741 100644 --- a/src/full-codegen.cc +++ b/src/full-codegen.cc @@ -96,11 +96,6 @@ void BreakableStatementChecker::VisitWithStatement(WithStatement* stmt) { } -void BreakableStatementChecker::VisitExitContextStatement( - ExitContextStatement* stmt) { -} - - void BreakableStatementChecker::VisitSwitchStatement(SwitchStatement* stmt) { // Switch statements breakable if the tag expression is. Visit(stmt->tag()); @@ -190,9 +185,9 @@ void BreakableStatementChecker::VisitArrayLiteral(ArrayLiteral* expr) { void BreakableStatementChecker::VisitAssignment(Assignment* expr) { // If assigning to a property (including a global property) the assignment is // breakable. - Variable* var = expr->target()->AsVariableProxy()->AsVariable(); + VariableProxy* proxy = expr->target()->AsVariableProxy(); Property* prop = expr->target()->AsProperty(); - if (prop != NULL || (var != NULL && var->is_global())) { + if (prop != NULL || (proxy != NULL && proxy->var()->IsUnallocated())) { is_breakable_ = true; return; } @@ -291,11 +286,13 @@ bool FullCodeGenerator::MakeCode(CompilationInfo* info) { } 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); @@ -395,26 +392,6 @@ void FullCodeGenerator::RecordStackCheck(int ast_id) { } -int FullCodeGenerator::SlotOffset(Slot* slot) { - ASSERT(slot != NULL); - // Offset is negative because higher indexes are at lower addresses. - int offset = -slot->index() * kPointerSize; - // Adjust by a (parameter or local) base offset. - switch (slot->type()) { - case Slot::PARAMETER: - offset += (info_->scope()->num_parameters() + 1) * kPointerSize; - break; - case Slot::LOCAL: - offset += JavaScriptFrameConstants::kLocal0Offset; - break; - case Slot::CONTEXT: - case Slot::LOOKUP: - UNREACHABLE(); - } - return offset; -} - - bool FullCodeGenerator::ShouldInlineSmiCase(Token::Value op) { // Inline smi case inside loops, but not division and modulo which // are too complicated and take up too much space. @@ -529,34 +506,21 @@ void FullCodeGenerator::DoTest(const TestContext* context) { void FullCodeGenerator::VisitDeclarations( ZoneList<Declaration*>* declarations) { int length = declarations->length(); - int globals = 0; + int global_count = 0; for (int i = 0; i < length; i++) { Declaration* decl = declarations->at(i); - Variable* var = decl->proxy()->var(); - Slot* slot = var->AsSlot(); - - // If it was not possible to allocate the variable at compile - // time, we need to "declare" it at runtime to make sure it - // actually exists in the local context. - if ((slot != NULL && slot->type() == Slot::LOOKUP) || !var->is_global()) { - VisitDeclaration(decl); - } else { - // Count global variables and functions for later processing - globals++; - } + EmitDeclaration(decl->proxy(), decl->mode(), decl->fun(), &global_count); } - // Compute array of global variable and function declarations. - // Do nothing in case of no declared global functions or variables. - if (globals > 0) { + // Batch declare global functions and variables. + if (global_count > 0) { Handle<FixedArray> array = - isolate()->factory()->NewFixedArray(2 * globals, TENURED); + isolate()->factory()->NewFixedArray(2 * global_count, TENURED); for (int j = 0, i = 0; i < length; i++) { Declaration* decl = declarations->at(i); Variable* var = decl->proxy()->var(); - Slot* slot = var->AsSlot(); - if ((slot == NULL || slot->type() != Slot::LOOKUP) && var->is_global()) { + if (var->IsUnallocated()) { array->set(j++, *(var->name())); if (decl->fun() == NULL) { if (var->mode() == Variable::CONST) { @@ -578,12 +542,21 @@ void FullCodeGenerator::VisitDeclarations( } } // Invoke the platform-dependent code generator to do the actual - // declaration the global variables and functions. + // declaration the global functions and variables. DeclareGlobals(array); } } +int FullCodeGenerator::DeclareGlobalsFlags() { + int flags = 0; + if (is_eval()) flags |= kDeclareGlobalsEvalFlag; + if (is_strict_mode()) flags |= kDeclareGlobalsStrictModeFlag; + if (is_native()) flags |= kDeclareGlobalsNativeFlag; + return flags; +} + + void FullCodeGenerator::SetFunctionPosition(FunctionLiteral* fun) { CodeGenerator::RecordPositions(masm_, fun->start_position()); } @@ -842,10 +815,11 @@ void FullCodeGenerator::VisitInCurrentContext(Expression* expr) { void FullCodeGenerator::VisitBlock(Block* stmt) { Comment cmnt(masm_, "[ Block"); - Breakable nested_statement(this, stmt); + NestedBlock nested_block(this, stmt); SetStatementPosition(stmt); Scope* saved_scope = scope(); + // Push a block context when entering a block with block scoped variables. if (stmt->block_scope() != NULL) { { Comment cmnt(masm_, "[ Extend block context"); scope_ = stmt->block_scope(); @@ -862,8 +836,16 @@ void FullCodeGenerator::VisitBlock(Block* stmt) { PrepareForBailoutForId(stmt->EntryId(), NO_REGISTERS); VisitStatements(stmt->statements()); scope_ = saved_scope; - __ bind(nested_statement.break_label()); + __ bind(nested_block.break_label()); PrepareForBailoutForId(stmt->ExitId(), NO_REGISTERS); + + // Pop block context if necessary. + if (stmt->block_scope() != NULL) { + LoadContextField(context_register(), Context::PREVIOUS_INDEX); + // Update local stack frame context field. + StoreToFrameField(StandardFrameConstants::kContextOffset, + context_register()); + } } @@ -1004,17 +986,6 @@ void FullCodeGenerator::VisitWithStatement(WithStatement* stmt) { } -void FullCodeGenerator::VisitExitContextStatement(ExitContextStatement* stmt) { - Comment cmnt(masm_, "[ ExitContextStatement"); - SetStatementPosition(stmt); - - // Pop context. - LoadContextField(context_register(), Context::PREVIOUS_INDEX); - // Update local stack frame context field. - StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); -} - - void FullCodeGenerator::VisitDoWhileStatement(DoWhileStatement* stmt) { Comment cmnt(masm_, "[ DoWhileStatement"); SetStatementPosition(stmt); @@ -1162,6 +1133,9 @@ void FullCodeGenerator::VisitTryCatchStatement(TryCatchStatement* stmt) { { WithOrCatch body(this); Visit(stmt->catch_block()); } + // Restore the context. + LoadContextField(context_register(), Context::PREVIOUS_INDEX); + StoreToFrameField(StandardFrameConstants::kContextOffset, context_register()); scope_ = saved_scope; __ jmp(&done); diff --git a/src/full-codegen.h b/src/full-codegen.h index 0ed26a14..803c6187 100644 --- a/src/full-codegen.h +++ b/src/full-codegen.h @@ -191,6 +191,22 @@ class FullCodeGenerator: public AstVisitor { Label continue_label_; }; + // A nested block statement. + class NestedBlock : public Breakable { + public: + NestedBlock(FullCodeGenerator* codegen, Block* block) + : Breakable(codegen, block) { + } + virtual ~NestedBlock() {} + + virtual NestedStatement* Exit(int* stack_depth, int* context_length) { + if (statement()->AsBlock()->block_scope() != NULL) { + ++(*context_length); + } + return previous_; + }; + }; + // The try block of a try/catch statement. class TryCatch : public NestedStatement { public: @@ -288,10 +304,6 @@ class FullCodeGenerator: public AstVisitor { // with a GC-safe value. void ClearAccumulator(); - // Compute the frame pointer relative offset for a given local or - // parameter slot. - int SlotOffset(Slot* slot); - // Determine whether or not to inline the smi case for the given // operation. bool ShouldInlineSmiCase(Token::Value op); @@ -321,13 +333,29 @@ class FullCodeGenerator: public AstVisitor { Label* fall_through); #endif // V8_TARGET_ARCH_MIPS - void Move(Slot* dst, Register source, Register scratch1, Register scratch2); - void Move(Register dst, Slot* source); - - // Return an operand used to read/write to a known (ie, non-LOOKUP) slot. - // May emit code to traverse the context chain, destroying the scratch - // register. - MemOperand EmitSlotSearch(Slot* slot, Register scratch); + // Load the value of a known (PARAMETER, LOCAL, or CONTEXT) variable into + // a register. Emits a context chain walk if if necessary (so does + // SetVar) so avoid calling both on the same variable. + void GetVar(Register destination, Variable* var); + + // Assign to a known (PARAMETER, LOCAL, or CONTEXT) variable. If it's in + // the context, the write barrier will be emitted and source, scratch0, + // scratch1 will be clobbered. Emits a context chain walk if if necessary + // (so does GetVar) so avoid calling both on the same variable. + void SetVar(Variable* var, + Register source, + Register scratch0, + Register scratch1); + + // An operand used to read/write a stack-allocated (PARAMETER or LOCAL) + // variable. Writing does not need the write barrier. + MemOperand StackOperand(Variable* var); + + // An operand used to read/write a known (PARAMETER, LOCAL, or CONTEXT) + // variable. May emit code to traverse the context chain, loading the + // found context into the scratch register. Writing to this operand will + // need the write barrier if location is CONTEXT. + MemOperand VarOperand(Variable* var, Register scratch); // Forward the bailout responsibility for the given expression to // the next child visited (which must be in a test context). @@ -358,6 +386,7 @@ class FullCodeGenerator: public AstVisitor { void VisitDeclarations(ZoneList<Declaration*>* declarations); void DeclareGlobals(Handle<FixedArray> pairs); + int DeclareGlobalsFlags(); // Try to perform a comparison as a fast inlined literal compare if // the operands allow it. Returns true if the compare operations @@ -402,9 +431,10 @@ class FullCodeGenerator: public AstVisitor { // Platform-specific code for a variable, constant, or function // declaration. Functions have an initial value. - void EmitDeclaration(Variable* variable, + void EmitDeclaration(VariableProxy* proxy, Variable::Mode mode, - FunctionLiteral* function); + FunctionLiteral* function, + int* global_count); // Platform-specific code for checking the stack limit at the back edge of // a loop. @@ -435,14 +465,14 @@ class FullCodeGenerator: public AstVisitor { #undef EMIT_INLINE_RUNTIME_CALL // Platform-specific code for loading variables. - void EmitLoadGlobalSlotCheckExtensions(Slot* slot, - TypeofState typeof_state, - Label* slow); - MemOperand ContextSlotOperandCheckExtensions(Slot* slot, Label* slow); - void EmitDynamicLoadFromSlotFastCase(Slot* slot, - TypeofState typeof_state, - Label* slow, - Label* done); + void EmitLoadGlobalCheckExtensions(Variable* var, + TypeofState typeof_state, + Label* slow); + MemOperand ContextSlotOperandCheckExtensions(Variable* var, Label* slow); + void EmitDynamicLookupFastCase(Variable* var, + TypeofState typeof_state, + Label* slow, + Label* done); void EmitVariableLoad(VariableProxy* proxy); enum ResolveEvalFlag { @@ -555,6 +585,7 @@ class FullCodeGenerator: public AstVisitor { Handle<Script> script() { return info_->script(); } bool is_eval() { return info_->is_eval(); } + bool is_native() { return info_->is_native(); } bool is_strict_mode() { return function()->strict_mode(); } StrictModeFlag strict_mode_flag() { return is_strict_mode() ? kStrictMode : kNonStrictMode; @@ -618,11 +649,11 @@ class FullCodeGenerator: public AstVisitor { // this expression context. virtual void Plug(bool flag) const = 0; - // Emit code to convert a pure value (in a register, slot, as a literal, - // or on top of the stack) into the result expected according to this - // expression context. + // Emit code to convert a pure value (in a register, known variable + // location, as a literal, or on top of the stack) into the result + // expected according to this expression context. virtual void Plug(Register reg) const = 0; - virtual void Plug(Slot* slot) const = 0; + virtual void Plug(Variable* var) const = 0; virtual void Plug(Handle<Object> lit) const = 0; virtual void Plug(Heap::RootListIndex index) const = 0; virtual void PlugTOS() const = 0; @@ -680,7 +711,7 @@ class FullCodeGenerator: public AstVisitor { virtual void Plug(bool flag) const; virtual void Plug(Register reg) const; virtual void Plug(Label* materialize_true, Label* materialize_false) const; - virtual void Plug(Slot* slot) const; + virtual void Plug(Variable* var) const; virtual void Plug(Handle<Object> lit) const; virtual void Plug(Heap::RootListIndex) const; virtual void PlugTOS() const; @@ -703,7 +734,7 @@ class FullCodeGenerator: public AstVisitor { virtual void Plug(bool flag) const; virtual void Plug(Register reg) const; virtual void Plug(Label* materialize_true, Label* materialize_false) const; - virtual void Plug(Slot* slot) const; + virtual void Plug(Variable* var) const; virtual void Plug(Handle<Object> lit) const; virtual void Plug(Heap::RootListIndex) const; virtual void PlugTOS() const; @@ -744,7 +775,7 @@ class FullCodeGenerator: public AstVisitor { virtual void Plug(bool flag) const; virtual void Plug(Register reg) const; virtual void Plug(Label* materialize_true, Label* materialize_false) const; - virtual void Plug(Slot* slot) const; + virtual void Plug(Variable* var) const; virtual void Plug(Handle<Object> lit) const; virtual void Plug(Heap::RootListIndex) const; virtual void PlugTOS() const; @@ -774,7 +805,7 @@ class FullCodeGenerator: public AstVisitor { virtual void Plug(bool flag) const; virtual void Plug(Register reg) const; virtual void Plug(Label* materialize_true, Label* materialize_false) const; - virtual void Plug(Slot* slot) const; + virtual void Plug(Variable* var) const; virtual void Plug(Handle<Object> lit) const; virtual void Plug(Heap::RootListIndex) const; virtual void PlugTOS() const; diff --git a/src/gdb-jit.cc b/src/gdb-jit.cc index 4d57e254..68cb0533 100644 --- a/src/gdb-jit.cc +++ b/src/gdb-jit.cc @@ -993,7 +993,7 @@ class CodeDescription BASE_EMBEDDED { } #endif - SmartPointer<char> GetFilename() { + SmartArrayPointer<char> GetFilename() { return String::cast(script_->name())->ToCString(); } @@ -1991,7 +1991,7 @@ void GDBJITInterface::AddCode(Handle<String> name, GetScriptLineNumber(script, 0); if (!name.is_null()) { - SmartPointer<char> name_cstring = name->ToCString(DISALLOW_NULLS); + SmartArrayPointer<char> name_cstring = name->ToCString(DISALLOW_NULLS); AddCode(*name_cstring, *code, GDBJITInterface::FUNCTION, *script, info); } else { AddCode("", *code, GDBJITInterface::FUNCTION, *script, info); diff --git a/src/globals.h b/src/globals.h index 7e41f976..6c6966ae 100644 --- a/src/globals.h +++ b/src/globals.h @@ -28,6 +28,35 @@ #ifndef V8_GLOBALS_H_ #define V8_GLOBALS_H_ +// Define V8_INFINITY +#define V8_INFINITY INFINITY + +// GCC specific stuff +#ifdef __GNUC__ + +#define __GNUC_VERSION_FOR_INFTY__ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) + +// Unfortunately, the INFINITY macro cannot be used with the '-pedantic' +// warning flag and certain versions of GCC due to a bug: +// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11931 +// For now, we use the more involved template-based version from <limits>, but +// only when compiling with GCC versions affected by the bug (2.96.x - 4.0.x) +// __GNUC_PREREQ is not defined in GCC for Mac OS X, so we define our own macro +#if __GNUC_VERSION_FOR_INFTY__ >= 29600 && __GNUC_VERSION_FOR_INFTY__ < 40100 +#include <limits> +#undef V8_INFINITY +#define V8_INFINITY std::numeric_limits<double>::infinity() +#endif +#undef __GNUC_VERSION_FOR_INFTY__ + +#endif // __GNUC__ + +#ifdef _MSC_VER +#undef V8_INFINITY +#define V8_INFINITY HUGE_VAL +#endif + + #include "../include/v8stdint.h" namespace v8 { @@ -226,10 +255,6 @@ const int kBinary32MinExponent = 0x01; const int kBinary32MantissaBits = 23; const int kBinary32ExponentShift = 23; -// Quiet NaNs have bits 51 to 62 set, possibly the sign bit, and no -// other bits set. -const uint64_t kQuietNaNMask = static_cast<uint64_t>(0xfff) << 51; - // ASCII/UC16 constants // Code-point values in Unicode 4.0 are 21 bits wide. typedef uint16_t uc16; diff --git a/src/handles.cc b/src/handles.cc index 8c6439b2..35c363c1 100644 --- a/src/handles.cc +++ b/src/handles.cc @@ -921,16 +921,13 @@ bool CompileLazyShared(Handle<SharedFunctionInfo> shared, } -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 @@ static bool CompileLazyFunction(Handle<JSFunction> function, } -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 9bb3b1f1..7eaf4de9 100644 --- a/src/handles.h +++ b/src/handles.h @@ -363,8 +363,6 @@ bool CompileLazyShared(Handle<SharedFunctionInfo> shared, 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 279f30b4..d0185930 100644 --- a/src/heap.cc +++ b/src/heap.cc @@ -41,7 +41,6 @@ #include "natives.h" #include "objects-visiting.h" #include "runtime-profiler.h" -#include "scanner-base.h" #include "scopeinfo.h" #include "snapshot.h" #include "v8threads.h" @@ -70,11 +69,11 @@ Heap::Heap() : isolate_(NULL), // semispace_size_ should be a power of 2 and old_generation_size_ should be // a multiple of Page::kPageSize. -#if 0//defined(ANDROID) +#if defined(ANDROID) reserved_semispace_size_(2*MB), max_semispace_size_(2*MB), initial_semispace_size_(128*KB), - max_old_generation_size_(512*MB), + max_old_generation_size_(192*MB), max_executable_size_(max_old_generation_size_), code_range_size_(0), #elif defined(V8_TARGET_ARCH_X64) @@ -842,6 +841,7 @@ void Heap::MarkCompactPrologue(bool is_compacting) { isolate_->keyed_lookup_cache()->Clear(); isolate_->context_slot_cache()->Clear(); isolate_->descriptor_lookup_cache()->Clear(); + StringSplitCache::Clear(string_split_cache()); isolate_->compilation_cache()->MarkCompactPrologue(); @@ -1627,7 +1627,7 @@ MaybeObject* Heap::AllocateMap(InstanceType instance_type, int instance_size) { map->set_unused_property_fields(0); map->set_bit_field(0); map->set_bit_field2(1 << Map::kIsExtensible); - map->set_elements_kind(JSObject::FAST_ELEMENTS); + map->set_elements_kind(FAST_ELEMENTS); // If the map object is aligned fill the padding area with Smi 0 objects. if (Map::kPadStart < Map::kSize) { @@ -2223,6 +2223,13 @@ bool Heap::CreateInitialObjects() { } set_single_character_string_cache(FixedArray::cast(obj)); + // Allocate cache for string split. + { MaybeObject* maybe_obj = + AllocateFixedArray(StringSplitCache::kStringSplitCacheSize, TENURED); + if (!maybe_obj->ToObject(&obj)) return false; + } + set_string_split_cache(FixedArray::cast(obj)); + // Allocate cache for external strings pointing to native source code. { MaybeObject* maybe_obj = AllocateFixedArray(Natives::GetBuiltinsCount()); if (!maybe_obj->ToObject(&obj)) return false; @@ -2248,6 +2255,75 @@ bool Heap::CreateInitialObjects() { } +Object* StringSplitCache::Lookup( + FixedArray* cache, String* string, String* pattern) { + if (!string->IsSymbol() || !pattern->IsSymbol()) return Smi::FromInt(0); + uint32_t hash = string->Hash(); + uint32_t index = ((hash & (kStringSplitCacheSize - 1)) & + ~(kArrayEntriesPerCacheEntry - 1)); + if (cache->get(index + kStringOffset) == string && + cache->get(index + kPatternOffset) == pattern) { + return cache->get(index + kArrayOffset); + } + index = ((index + kArrayEntriesPerCacheEntry) & (kStringSplitCacheSize - 1)); + if (cache->get(index + kStringOffset) == string && + cache->get(index + kPatternOffset) == pattern) { + return cache->get(index + kArrayOffset); + } + return Smi::FromInt(0); +} + + +void StringSplitCache::Enter(Heap* heap, + FixedArray* cache, + String* string, + String* pattern, + FixedArray* array) { + if (!string->IsSymbol() || !pattern->IsSymbol()) return; + uint32_t hash = string->Hash(); + uint32_t index = ((hash & (kStringSplitCacheSize - 1)) & + ~(kArrayEntriesPerCacheEntry - 1)); + if (cache->get(index + kStringOffset) == Smi::FromInt(0)) { + cache->set(index + kStringOffset, string); + cache->set(index + kPatternOffset, pattern); + cache->set(index + kArrayOffset, array); + } else { + uint32_t index2 = + ((index + kArrayEntriesPerCacheEntry) & (kStringSplitCacheSize - 1)); + if (cache->get(index2 + kStringOffset) == Smi::FromInt(0)) { + cache->set(index2 + kStringOffset, string); + cache->set(index2 + kPatternOffset, pattern); + cache->set(index2 + kArrayOffset, array); + } else { + cache->set(index2 + kStringOffset, Smi::FromInt(0)); + cache->set(index2 + kPatternOffset, Smi::FromInt(0)); + cache->set(index2 + kArrayOffset, Smi::FromInt(0)); + cache->set(index + kStringOffset, string); + cache->set(index + kPatternOffset, pattern); + cache->set(index + kArrayOffset, array); + } + } + if (array->length() < 100) { // Limit how many new symbols we want to make. + for (int i = 0; i < array->length(); i++) { + String* str = String::cast(array->get(i)); + Object* symbol; + MaybeObject* maybe_symbol = heap->LookupSymbol(str); + if (maybe_symbol->ToObject(&symbol)) { + array->set(i, symbol); + } + } + } + array->set_map(heap->fixed_cow_array_map()); +} + + +void StringSplitCache::Clear(FixedArray* cache) { + for (int i = 0; i < kStringSplitCacheSize; i++) { + cache->set(i, Smi::FromInt(0)); + } +} + + MaybeObject* Heap::InitializeNumberStringCache() { // Compute the size of the number string cache based on the max heap size. // max_semispace_size_ == 512 KB => number_string_cache_size = 32. @@ -3339,11 +3415,36 @@ MaybeObject* Heap::AllocateJSProxy(Object* handler, Object* prototype) { 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; } @@ -3488,16 +3589,19 @@ MaybeObject* Heap::CopyJSObject(JSObject* source) { } -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()); @@ -3514,6 +3618,28 @@ MaybeObject* Heap::ReinitializeJSProxyAsJSObject(JSProxy* object) { // 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; } @@ -3546,6 +3672,9 @@ MaybeObject* Heap::ReinitializeJSGlobalProxy(JSFunction* constructor, MaybeObject* Heap::AllocateStringFromAscii(Vector<const char> string, PretenureFlag pretenure) { + if (string.length() == 1) { + return Heap::LookupSingleCharacterStringFromCode(string[0]); + } Object* result; { MaybeObject* maybe_result = AllocateRawAsciiString(string.length(), pretenure); @@ -77,6 +77,7 @@ inline Heap* _inline_get_heap_(); V(Object, instanceof_cache_map, InstanceofCacheMap) \ V(Object, instanceof_cache_answer, InstanceofCacheAnswer) \ V(FixedArray, single_character_string_cache, SingleCharacterStringCache) \ + V(FixedArray, string_split_cache, StringSplitCache) \ V(Object, termination_exception, TerminationException) \ V(FixedArray, empty_fixed_array, EmptyFixedArray) \ V(ByteArray, empty_byte_array, EmptyByteArray) \ @@ -225,8 +226,7 @@ inline Heap* _inline_get_heap_(); V(closure_symbol, "(closure)") \ V(use_strict, "use strict") \ V(dot_symbol, ".") \ - V(anonymous_function_symbol, "(anonymous function)") \ - V(block_scope_symbol, ".block") + V(anonymous_function_symbol, "(anonymous function)") // Forward declarations. class GCTracer; @@ -440,17 +440,25 @@ class Heap { // 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 @@ -2177,6 +2185,27 @@ class GCTracer BASE_EMBEDDED { }; +class StringSplitCache { + public: + static Object* Lookup(FixedArray* cache, String* string, String* pattern); + static void Enter(Heap* heap, + FixedArray* cache, + String* string, + String* pattern, + FixedArray* array); + static void Clear(FixedArray* cache); + static const int kStringSplitCacheSize = 0x100; + + private: + static const int kArrayEntriesPerCacheEntry = 4; + static const int kStringOffset = 0; + static const int kPatternOffset = 1; + static const int kArrayOffset = 2; + + static MaybeObject* WrapFixedArrayInJSArray(Object* fixed_array); +}; + + class TranscendentalCache { public: enum Type {ACOS, ASIN, ATAN, COS, EXP, LOG, SIN, TAN, kNumberOfCaches}; diff --git a/src/hydrogen-instructions.cc b/src/hydrogen-instructions.cc index d3cc8a62..5630ce39 100644 --- a/src/hydrogen-instructions.cc +++ b/src/hydrogen-instructions.cc @@ -425,7 +425,7 @@ void HValue::PrintRangeTo(StringStream* stream) { void HValue::PrintChangesTo(StringStream* stream) { - int changes_flags = (flags() & HValue::ChangesFlagsMask()); + int changes_flags = ChangesFlags(); if (changes_flags == 0) return; stream->Add(" changes["); if (changes_flags == AllSideEffects()) { @@ -512,9 +512,7 @@ void HInstruction::PrintTo(StringStream* stream) { void HInstruction::PrintMnemonicTo(StringStream* stream) { - stream->Add("%s", Mnemonic()); - if (HasSideEffects()) stream->Add("*"); - stream->Add(" "); + stream->Add("%s ", Mnemonic()); } @@ -791,6 +789,13 @@ void HChange::PrintDataTo(StringStream* stream) { } +void HJSArrayLength::PrintDataTo(StringStream* stream) { + value()->PrintNameTo(stream); + stream->Add(" "); + typecheck()->PrintNameTo(stream); +} + + HValue* HCheckInstanceType::Canonicalize() { if (check_ == IS_STRING && !value()->type().IsUninitialized() && @@ -1020,11 +1025,14 @@ void HPhi::PrintTo(StringStream* stream) { value->PrintNameTo(stream); stream->Add(" "); } - stream->Add(" uses%d_%di_%dd_%dt]", + stream->Add(" uses%d_%di_%dd_%dt", UseCount(), int32_non_phi_uses() + int32_indirect_uses(), double_non_phi_uses() + double_indirect_uses(), tagged_non_phi_uses() + tagged_indirect_uses()); + stream->Add("%s%s]", + is_live() ? "_live" : "", + IsConvertibleToInteger() ? "" : "_ncti"); } @@ -1125,7 +1133,7 @@ void HDeoptimize::PrintDataTo(StringStream* stream) { void HEnterInlined::PrintDataTo(StringStream* stream) { - SmartPointer<char> name = function()->debug_name()->ToCString(); + SmartArrayPointer<char> name = function()->debug_name()->ToCString(); stream->Add("%s, id=%d", *name, function()->id()); } @@ -1299,6 +1307,12 @@ void HCompareIDAndBranch::PrintDataTo(StringStream* stream) { left()->PrintNameTo(stream); stream->Add(" "); right()->PrintNameTo(stream); + HControlInstruction::PrintDataTo(stream); +} + + +void HGoto::PrintDataTo(StringStream* stream) { + stream->Add("B%d", SuccessorAt(0)->block_id()); } @@ -1446,37 +1460,37 @@ void HLoadKeyedSpecializedArrayElement::PrintDataTo( external_pointer()->PrintNameTo(stream); stream->Add("."); switch (elements_kind()) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: stream->Add("byte"); break; - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: stream->Add("u_byte"); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: stream->Add("short"); break; - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: stream->Add("u_short"); break; - case JSObject::EXTERNAL_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: stream->Add("int"); break; - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: stream->Add("u_int"); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: stream->Add("float"); break; - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: stream->Add("double"); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: stream->Add("pixel"); break; - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -1541,37 +1555,37 @@ void HStoreKeyedSpecializedArrayElement::PrintDataTo( external_pointer()->PrintNameTo(stream); stream->Add("."); switch (elements_kind()) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: stream->Add("byte"); break; - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: stream->Add("u_byte"); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: stream->Add("short"); break; - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: stream->Add("u_short"); break; - case JSObject::EXTERNAL_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: stream->Add("int"); break; - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: stream->Add("u_int"); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: stream->Add("float"); break; - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: stream->Add("double"); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: stream->Add("pixel"); break; - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } diff --git a/src/hydrogen-instructions.h b/src/hydrogen-instructions.h index 76007d76..1bc28ba8 100644 --- a/src/hydrogen-instructions.h +++ b/src/hydrogen-instructions.h @@ -513,19 +513,6 @@ class HValue: public ZoneObject { static const int kChangesToDependsFlagsLeftShift = 1; - static int ChangesFlagsMask() { - int result = 0; - // Create changes mask. -#define DECLARE_DO(type) result |= (1 << kChanges##type); - GVN_FLAG_LIST(DECLARE_DO) -#undef DECLARE_DO - return result; - } - - static int DependsFlagsMask() { - return ConvertChangesToDependsFlags(ChangesFlagsMask()); - } - static int ConvertChangesToDependsFlags(int flags) { return flags << kChangesToDependsFlagsLeftShift; } @@ -629,6 +616,8 @@ class HValue: public ZoneObject { void ClearAllSideEffects() { flags_ &= ~AllSideEffects(); } bool HasSideEffects() const { return (flags_ & AllSideEffects()) != 0; } + int ChangesFlags() const { return flags_ & ChangesFlagsMask(); } + Range* range() const { return range_; } bool HasRange() const { return range_ != NULL; } void AddNewRange(Range* r); @@ -693,6 +682,15 @@ class HValue: public ZoneObject { } private: + static int ChangesFlagsMask() { + int result = 0; + // Create changes mask. +#define ADD_FLAG(type) result |= (1 << kChanges##type); + GVN_FLAG_LIST(ADD_FLAG) +#undef ADD_FLAG + return result; + } + // A flag mask to mark an instruction as having arbitrary side effects. static int AllSideEffects() { return ChangesFlagsMask() & ~(1 << kChangesOsrEntries); @@ -917,6 +915,8 @@ class HGoto: public HTemplateControlInstruction<1, 0> { return Representation::None(); } + virtual void PrintDataTo(StringStream* stream); + DECLARE_CONCRETE_INSTRUCTION(Goto) }; @@ -1696,7 +1696,10 @@ class HJSArrayLength: public HTemplateInstruction<2> { return Representation::Tagged(); } + virtual void PrintDataTo(StringStream* stream); + HValue* value() { return OperandAt(0); } + HValue* typecheck() { return OperandAt(1); } DECLARE_CONCRETE_INSTRUCTION(JSArrayLength) @@ -2208,6 +2211,13 @@ class HPhi: public HValue { is_convertible_to_integer_ = b; } + bool AllOperandsConvertibleToInteger() { + for (int i = 0; i < OperandCount(); ++i) { + if (!OperandAt(i)->IsConvertibleToInteger()) return false; + } + return true; + } + protected: virtual void DeleteFromGraph(); virtual void InternalSetOperandAt(int index, HValue* value) { @@ -3554,12 +3564,12 @@ class HLoadKeyedSpecializedArrayElement: public HTemplateInstruction<2> { public: HLoadKeyedSpecializedArrayElement(HValue* external_elements, HValue* key, - JSObject::ElementsKind elements_kind) + ElementsKind elements_kind) : elements_kind_(elements_kind) { SetOperandAt(0, external_elements); SetOperandAt(1, key); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS || - elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS || + elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { set_representation(Representation::Double()); } else { set_representation(Representation::Integer32()); @@ -3582,7 +3592,7 @@ class HLoadKeyedSpecializedArrayElement: public HTemplateInstruction<2> { HValue* external_pointer() { return OperandAt(0); } HValue* key() { return OperandAt(1); } - JSObject::ElementsKind elements_kind() const { return elements_kind_; } + ElementsKind elements_kind() const { return elements_kind_; } DECLARE_CONCRETE_INSTRUCTION(LoadKeyedSpecializedArrayElement) @@ -3595,7 +3605,7 @@ class HLoadKeyedSpecializedArrayElement: public HTemplateInstruction<2> { } private: - JSObject::ElementsKind elements_kind_; + ElementsKind elements_kind_; }; @@ -3775,7 +3785,7 @@ class HStoreKeyedSpecializedArrayElement: public HTemplateInstruction<3> { HStoreKeyedSpecializedArrayElement(HValue* external_elements, HValue* key, HValue* val, - JSObject::ElementsKind elements_kind) + ElementsKind elements_kind) : elements_kind_(elements_kind) { SetFlag(kChangesSpecializedArrayElements); SetOperandAt(0, external_elements); @@ -3790,8 +3800,8 @@ class HStoreKeyedSpecializedArrayElement: public HTemplateInstruction<3> { return Representation::External(); } else { bool float_or_double_elements = - elements_kind() == JSObject::EXTERNAL_FLOAT_ELEMENTS || - elements_kind() == JSObject::EXTERNAL_DOUBLE_ELEMENTS; + elements_kind() == EXTERNAL_FLOAT_ELEMENTS || + elements_kind() == EXTERNAL_DOUBLE_ELEMENTS; if (index == 2 && float_or_double_elements) { return Representation::Double(); } else { @@ -3803,12 +3813,12 @@ class HStoreKeyedSpecializedArrayElement: public HTemplateInstruction<3> { HValue* external_pointer() { return OperandAt(0); } HValue* key() { return OperandAt(1); } HValue* value() { return OperandAt(2); } - JSObject::ElementsKind elements_kind() const { return elements_kind_; } + ElementsKind elements_kind() const { return elements_kind_; } DECLARE_CONCRETE_INSTRUCTION(StoreKeyedSpecializedArrayElement) private: - JSObject::ElementsKind elements_kind_; + ElementsKind elements_kind_; }; diff --git a/src/hydrogen.cc b/src/hydrogen.cc index 5772141b..c625fba8 100644 --- a/src/hydrogen.cc +++ b/src/hydrogen.cc @@ -220,6 +220,17 @@ bool HBasicBlock::Dominates(HBasicBlock* other) const { } +int HBasicBlock::LoopNestingDepth() const { + const HBasicBlock* current = this; + int result = (current->IsLoopHeader()) ? 1 : 0; + while (current->parent_loop_header() != NULL) { + current = current->parent_loop_header(); + result++; + } + return result; +} + + void HBasicBlock::PostProcessLoopHeader(IterationStatement* stmt) { ASSERT(IsLoopHeader()); @@ -638,8 +649,7 @@ Handle<Code> HGraph::Compile(CompilationInfo* info) { 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); @@ -840,7 +850,7 @@ void HGraph::EliminateUnreachablePhis() { } -bool HGraph::CheckArgumentsPhiUses() { +bool HGraph::CheckPhis() { int block_count = blocks_.length(); for (int i = 0; i < block_count; ++i) { for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { @@ -853,11 +863,13 @@ bool HGraph::CheckArgumentsPhiUses() { } -bool HGraph::CheckConstPhiUses() { +bool HGraph::CollectPhis() { int block_count = blocks_.length(); + phi_list_ = new ZoneList<HPhi*>(block_count); for (int i = 0; i < block_count; ++i) { for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { HPhi* phi = blocks_[i]->phis()->at(j); + phi_list_->Add(phi); // Check for the hole value (from an uninitialized const). for (int k = 0; k < phi->OperandCount(); k++) { if (phi->OperandAt(k) == GetConstantHole()) return false; @@ -868,18 +880,6 @@ bool HGraph::CheckConstPhiUses() { } -void HGraph::CollectPhis() { - int block_count = blocks_.length(); - phi_list_ = new ZoneList<HPhi*>(block_count); - for (int i = 0; i < block_count; ++i) { - for (int j = 0; j < blocks_[i]->phis()->length(); ++j) { - HPhi* phi = blocks_[i]->phis()->at(j); - phi_list_->Add(phi); - } - } -} - - void HGraph::InferTypes(ZoneList<HValue*>* worklist) { BitVector in_worklist(GetMaximumValueID()); for (int i = 0; i < worklist->length(); ++i) { @@ -1392,7 +1392,7 @@ void HGlobalValueNumberer::ComputeBlockSideEffects() { int id = block->block_id(); int side_effects = 0; while (instr != NULL) { - side_effects |= (instr->flags() & HValue::ChangesFlagsMask()); + side_effects |= instr->ChangesFlags(); instr = instr->next(); } block_side_effects_[id] |= side_effects; @@ -1512,7 +1512,7 @@ void HGlobalValueNumberer::AnalyzeBlock(HBasicBlock* block, HValueMap* map) { HInstruction* instr = block->first(); while (instr != NULL) { HInstruction* next = instr->next(); - int flags = (instr->flags() & HValue::ChangesFlagsMask()); + int flags = instr->ChangesFlags(); if (flags != 0) { ASSERT(!instr->CheckFlag(HValue::kUseGVN)); // Clear all instructions in the map that are affected by side effects. @@ -1651,18 +1651,20 @@ Representation HInferRepresentation::TryChange(HValue* value) { int non_tagged_count = double_count + int32_count; // If a non-loop phi has tagged uses, don't convert it to untagged. - if (value->IsPhi() && !value->block()->IsLoopHeader()) { - if (tagged_count > 0) return Representation::None(); + if (value->IsPhi() && !value->block()->IsLoopHeader() && tagged_count > 0) { + return Representation::None(); } - if (non_tagged_count >= tagged_count) { - if (int32_count > 0) { - if (!value->IsPhi() || value->IsConvertibleToInteger()) { - return Representation::Integer32(); - } - } - if (double_count > 0) return Representation::Double(); + // Prefer unboxing over boxing, the latter is more expensive. + if (tagged_count > non_tagged_count) Representation::None(); + + // Prefer Integer32 over Double, if possible. + if (int32_count > 0 && value->IsConvertibleToInteger()) { + return Representation::Integer32(); } + + if (double_count > 0) return Representation::Double(); + return Representation::None(); } @@ -1688,7 +1690,9 @@ void HInferRepresentation::Analyze() { bool change = true; while (change) { change = false; - for (int i = 0; i < phi_count; ++i) { + // We normally have far more "forward edges" than "backward edges", + // so we terminate faster when we walk backwards. + for (int i = phi_count - 1; i >= 0; --i) { HPhi* phi = phi_list->at(i); for (HUseIterator it(phi->uses()); !it.Done(); it.Advance()) { HValue* use = it.value(); @@ -1701,40 +1705,25 @@ void HInferRepresentation::Analyze() { } } - // (3) Sum up the non-phi use counts of all connected phis. Don't include - // the non-phi uses of the phi itself. + // (3) Use the phi reachability information from step 2 to + // (a) sum up the non-phi use counts of all connected phis. + // (b) push information about values which can't be converted to integer + // without deoptimization through the phi use-def chains, avoiding + // unnecessary deoptimizations later. for (int i = 0; i < phi_count; ++i) { HPhi* phi = phi_list->at(i); + bool cti = phi->AllOperandsConvertibleToInteger(); for (BitVector::Iterator it(connected_phis.at(i)); !it.Done(); it.Advance()) { int index = it.Current(); - if (index != i) { - HPhi* it_use = phi_list->at(it.Current()); - phi->AddNonPhiUsesFrom(it_use); - } - } - } - - // (4) Compute phis that definitely can't be converted to integer - // without deoptimization and mark them to avoid unnecessary deoptimization. - change = true; - while (change) { - change = false; - for (int i = 0; i < phi_count; ++i) { - HPhi* phi = phi_list->at(i); - for (int j = 0; j < phi->OperandCount(); ++j) { - if (phi->IsConvertibleToInteger() && - !phi->OperandAt(j)->IsConvertibleToInteger()) { - phi->set_is_convertible_to_integer(false); - change = true; - break; - } - } + HPhi* it_use = phi_list->at(it.Current()); + if (index != i) phi->AddNonPhiUsesFrom(it_use); // Don't count twice! + if (!cti) it_use->set_is_convertible_to_integer(false); } } - + // Initialize work list for (int i = 0; i < graph_->blocks()->length(); ++i) { HBasicBlock* block = graph_->blocks()->at(i); const ZoneList<HPhi*>* phis = block->phis(); @@ -1749,6 +1738,7 @@ void HInferRepresentation::Analyze() { } } + // Do a fixed point iteration, trying to improve representations while (!worklist_.is_empty()) { HValue* current = worklist_.RemoveLast(); in_worklist_.Remove(current->id()); @@ -2214,7 +2204,8 @@ void TestContext::BuildBranch(HValue* value) { void HGraphBuilder::Bailout(const char* reason) { if (FLAG_trace_bailout) { - SmartPointer<char> name(info()->shared_info()->DebugName()->ToCString()); + SmartArrayPointer<char> name( + info()->shared_info()->DebugName()->ToCString()); PrintF("Bailout in HGraphBuilder: @\"%s\": %s\n", *name, reason); } SetStackOverflow(); @@ -2286,10 +2277,6 @@ HGraph* HGraphBuilder::CreateGraph() { return NULL; } SetupScope(scope); - VisitDeclarations(scope->declarations()); - HValue* context = environment()->LookupContext(); - AddInstruction( - new(zone()) HStackCheck(context, HStackCheck::kFunctionEntry)); // Add an edge to the body entry. This is warty: the graph's start // environment will be used by the Lithium translation as the initial @@ -2311,6 +2298,19 @@ HGraph* HGraphBuilder::CreateGraph() { current_block()->Goto(body_entry); body_entry->SetJoinId(AstNode::kFunctionEntryId); set_current_block(body_entry); + + // Handle implicit declaration of the function name in named function + // expressions before other declarations. + if (scope->is_function_scope() && scope->function() != NULL) { + HandleDeclaration(scope->function(), Variable::CONST, NULL); + } + VisitDeclarations(scope->declarations()); + AddSimulate(AstNode::kDeclarationsId); + + HValue* context = environment()->LookupContext(); + AddInstruction( + new(zone()) HStackCheck(context, HStackCheck::kFunctionEntry)); + VisitStatements(info()->function()->body()); if (HasStackOverflow()) return NULL; @@ -2324,17 +2324,16 @@ HGraph* HGraphBuilder::CreateGraph() { graph()->OrderBlocks(); graph()->AssignDominators(); graph()->PropagateDeoptimizingMark(); - if (!graph()->CheckConstPhiUses()) { - Bailout("Unsupported phi use of const variable"); - return NULL; - } graph()->EliminateRedundantPhis(); - if (!graph()->CheckArgumentsPhiUses()) { - Bailout("Unsupported phi use of arguments"); + if (!graph()->CheckPhis()) { + Bailout("Unsupported phi use of arguments object"); return NULL; } if (FLAG_eliminate_dead_phis) graph()->EliminateUnreachablePhis(); - graph()->CollectPhis(); + if (!graph()->CollectPhis()) { + Bailout("Unsupported phi use of uninitialized constant"); + return NULL; + } HInferRepresentation rep(graph()); rep.Analyze(); @@ -2465,14 +2464,6 @@ void HGraphBuilder::SetupScope(Scope* scope) { graph()->SetArgumentsObject(object); environment()->Bind(scope->arguments(), object); } - // Handle implicit declaration of the function name in named function - // expressions before other declarations. - if (scope->is_function_scope() && scope->function() != NULL) { - if (!scope->function()->IsStackAllocated()) { - return Bailout("unsupported declaration"); - } - environment()->Bind(scope->function(), graph()->GetConstantHole()); - } } @@ -2665,14 +2656,6 @@ void HGraphBuilder::VisitWithStatement(WithStatement* stmt) { } -void HGraphBuilder::VisitExitContextStatement(ExitContextStatement* stmt) { - ASSERT(!HasStackOverflow()); - ASSERT(current_block() != NULL); - ASSERT(current_block()->HasPredecessor()); - return Bailout("ExitContextStatement"); -} - - void HGraphBuilder::VisitSwitchStatement(SwitchStatement* stmt) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); @@ -3141,56 +3124,63 @@ void HGraphBuilder::VisitVariableProxy(VariableProxy* expr) { ASSERT(!HasStackOverflow()); ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); - Variable* variable = expr->AsVariable(); - if (variable == NULL) { - return Bailout("reference to rewritten variable"); - } else if (variable->mode() == Variable::LET) { + Variable* variable = expr->var(); + if (variable->mode() == Variable::LET) { return Bailout("reference to let variable"); - } else if (variable->IsStackAllocated()) { - HValue* value = environment()->Lookup(variable); - if (variable->mode() == Variable::CONST && - value == graph()->GetConstantHole()) { - return Bailout("reference to uninitialized const variable"); - } - return ast_context()->ReturnValue(value); - } else if (variable->IsContextSlot()) { - if (variable->mode() == Variable::CONST) { - return Bailout("reference to const context slot"); - } - HValue* context = BuildContextChainWalk(variable); - int index = variable->AsSlot()->index(); - HLoadContextSlot* instr = new(zone()) HLoadContextSlot(context, index); - return ast_context()->ReturnInstruction(instr, expr->id()); - } else if (variable->is_global()) { - LookupResult lookup; - GlobalPropertyAccess type = LookupGlobalProperty(variable, &lookup, false); + } + switch (variable->location()) { + case Variable::UNALLOCATED: { + LookupResult lookup; + GlobalPropertyAccess type = + LookupGlobalProperty(variable, &lookup, false); - if (type == kUseCell && - info()->global_object()->IsAccessCheckNeeded()) { - type = kUseGeneric; + if (type == kUseCell && + info()->global_object()->IsAccessCheckNeeded()) { + type = kUseGeneric; + } + + if (type == kUseCell) { + Handle<GlobalObject> global(info()->global_object()); + Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup)); + bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); + HLoadGlobalCell* instr = new(zone()) HLoadGlobalCell(cell, check_hole); + return ast_context()->ReturnInstruction(instr, expr->id()); + } else { + HValue* context = environment()->LookupContext(); + HGlobalObject* global_object = new(zone()) HGlobalObject(context); + AddInstruction(global_object); + HLoadGlobalGeneric* instr = + new(zone()) HLoadGlobalGeneric(context, + global_object, + variable->name(), + ast_context()->is_for_typeof()); + instr->set_position(expr->position()); + return ast_context()->ReturnInstruction(instr, expr->id()); + } } - if (type == kUseCell) { - Handle<GlobalObject> global(info()->global_object()); - Handle<JSGlobalPropertyCell> cell(global->GetPropertyCell(&lookup)); - bool check_hole = !lookup.IsDontDelete() || lookup.IsReadOnly(); - HLoadGlobalCell* instr = new(zone()) HLoadGlobalCell(cell, check_hole); - return ast_context()->ReturnInstruction(instr, expr->id()); - } else { - HValue* context = environment()->LookupContext(); - HGlobalObject* global_object = new(zone()) HGlobalObject(context); - AddInstruction(global_object); - HLoadGlobalGeneric* instr = - new(zone()) HLoadGlobalGeneric(context, - global_object, - variable->name(), - ast_context()->is_for_typeof()); - instr->set_position(expr->position()); - ASSERT(instr->HasSideEffects()); + case Variable::PARAMETER: + case Variable::LOCAL: { + HValue* value = environment()->Lookup(variable); + if (variable->mode() == Variable::CONST && + value == graph()->GetConstantHole()) { + return Bailout("reference to uninitialized const variable"); + } + return ast_context()->ReturnValue(value); + } + + case Variable::CONTEXT: { + if (variable->mode() == Variable::CONST) { + return Bailout("reference to const context slot"); + } + HValue* context = BuildContextChainWalk(variable); + HLoadContextSlot* instr = + new(zone()) HLoadContextSlot(context, variable->index()); return ast_context()->ReturnInstruction(instr, expr->id()); } - } else { - return Bailout("reference to a variable which requires dynamic lookup"); + + case Variable::LOOKUP: + return Bailout("reference to a variable which requires dynamic lookup"); } } @@ -3602,52 +3592,61 @@ void HGraphBuilder::HandleGlobalVariableAssignment(Variable* var, void HGraphBuilder::HandleCompoundAssignment(Assignment* expr) { Expression* target = expr->target(); VariableProxy* proxy = target->AsVariableProxy(); - Variable* var = proxy->AsVariable(); Property* prop = target->AsProperty(); - ASSERT(var == NULL || prop == NULL); + ASSERT(proxy == NULL || prop == NULL); // We have a second position recorded in the FullCodeGenerator to have // type feedback for the binary operation. BinaryOperation* operation = expr->binary_operation(); - if (var != NULL) { - if (var->mode() == Variable::CONST || - var->mode() == Variable::LET) { + if (proxy != NULL) { + Variable* var = proxy->var(); + if (var->mode() == Variable::CONST || var->mode() == Variable::LET) { return Bailout("unsupported let or const compound assignment"); } CHECK_ALIVE(VisitForValue(operation)); - if (var->is_global()) { - HandleGlobalVariableAssignment(var, - Top(), - expr->position(), - expr->AssignmentId()); - } else if (var->IsStackAllocated()) { - Bind(var, Top()); - } else if (var->IsContextSlot()) { - // Bail out if we try to mutate a parameter value in a function using - // the arguments object. We do not (yet) correctly handle the - // arguments property of the function. - if (info()->scope()->arguments() != NULL) { - // Parameters will rewrite to context slots. We have no direct way - // to detect that the variable is a parameter. - int count = info()->scope()->num_parameters(); - for (int i = 0; i < count; ++i) { - if (var == info()->scope()->parameter(i)) { - Bailout("assignment to parameter, function uses arguments object"); + switch (var->location()) { + case Variable::UNALLOCATED: + HandleGlobalVariableAssignment(var, + Top(), + expr->position(), + expr->AssignmentId()); + break; + + case Variable::PARAMETER: + case Variable::LOCAL: + Bind(var, Top()); + break; + + case Variable::CONTEXT: { + // Bail out if we try to mutate a parameter value in a function + // using the arguments object. We do not (yet) correctly handle the + // arguments property of the function. + if (info()->scope()->arguments() != NULL) { + // Parameters will be allocated to context slots. We have no + // direct way to detect that the variable is a parameter so we do + // a linear search of the parameter variables. + int count = info()->scope()->num_parameters(); + for (int i = 0; i < count; ++i) { + if (var == info()->scope()->parameter(i)) { + Bailout( + "assignment to parameter, function uses arguments object"); + } } } + + HValue* context = BuildContextChainWalk(var); + HStoreContextSlot* instr = + new(zone()) HStoreContextSlot(context, var->index(), Top()); + AddInstruction(instr); + if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + break; } - HValue* context = BuildContextChainWalk(var); - int index = var->AsSlot()->index(); - HStoreContextSlot* instr = - new(zone()) HStoreContextSlot(context, index, Top()); - AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); - } else { - return Bailout("compound assignment to lookup slot"); + case Variable::LOOKUP: + return Bailout("compound assignment to lookup slot"); } return ast_context()->ReturnValue(Pop()); @@ -3735,16 +3734,18 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { ASSERT(current_block() != NULL); ASSERT(current_block()->HasPredecessor()); VariableProxy* proxy = expr->target()->AsVariableProxy(); - Variable* var = proxy->AsVariable(); Property* prop = expr->target()->AsProperty(); - ASSERT(var == NULL || prop == NULL); + ASSERT(proxy == NULL || prop == NULL); if (expr->is_compound()) { HandleCompoundAssignment(expr); return; } - if (var != NULL) { + if (prop != NULL) { + HandlePropertyAssignment(expr); + } else if (proxy != NULL) { + Variable* var = proxy->var(); if (var->mode() == Variable::CONST) { if (expr->op() != Token::INIT_CONST) { return Bailout("non-initializer assignment to const"); @@ -3763,54 +3764,54 @@ void HGraphBuilder::VisitAssignment(Assignment* expr) { if (proxy->IsArguments()) return Bailout("assignment to arguments"); // Handle the assignment. - if (var->IsStackAllocated()) { - // We do not allow the arguments object to occur in a context where it - // may escape, but assignments to stack-allocated locals are - // permitted. - CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED)); - HValue* value = Pop(); - Bind(var, value); - return ast_context()->ReturnValue(value); + switch (var->location()) { + case Variable::UNALLOCATED: + CHECK_ALIVE(VisitForValue(expr->value())); + HandleGlobalVariableAssignment(var, + Top(), + expr->position(), + expr->AssignmentId()); + return ast_context()->ReturnValue(Pop()); + + case Variable::PARAMETER: + case Variable::LOCAL: { + // We do not allow the arguments object to occur in a context where it + // may escape, but assignments to stack-allocated locals are + // permitted. + CHECK_ALIVE(VisitForValue(expr->value(), ARGUMENTS_ALLOWED)); + HValue* value = Pop(); + Bind(var, value); + return ast_context()->ReturnValue(value); + } - } else if (var->IsContextSlot()) { - ASSERT(var->mode() != Variable::CONST); - // Bail out if we try to mutate a parameter value in a function using - // the arguments object. We do not (yet) correctly handle the - // arguments property of the function. - if (info()->scope()->arguments() != NULL) { - // Parameters will rewrite to context slots. We have no direct way - // to detect that the variable is a parameter. - int count = info()->scope()->num_parameters(); - for (int i = 0; i < count; ++i) { - if (var == info()->scope()->parameter(i)) { - Bailout("assignment to parameter, function uses arguments object"); + case Variable::CONTEXT: { + ASSERT(var->mode() != Variable::CONST); + // Bail out if we try to mutate a parameter value in a function using + // the arguments object. We do not (yet) correctly handle the + // arguments property of the function. + if (info()->scope()->arguments() != NULL) { + // Parameters will rewrite to context slots. We have no direct way + // to detect that the variable is a parameter. + int count = info()->scope()->num_parameters(); + for (int i = 0; i < count; ++i) { + if (var == info()->scope()->parameter(i)) { + return Bailout("assignment to parameter in arguments object"); + } } } - } - CHECK_ALIVE(VisitForValue(expr->value())); - HValue* context = BuildContextChainWalk(var); - int index = var->AsSlot()->index(); - HStoreContextSlot* instr = - new(zone()) HStoreContextSlot(context, index, Top()); - AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); - return ast_context()->ReturnValue(Pop()); - - } else if (var->is_global()) { - CHECK_ALIVE(VisitForValue(expr->value())); - HandleGlobalVariableAssignment(var, - Top(), - expr->position(), - expr->AssignmentId()); - return ast_context()->ReturnValue(Pop()); + CHECK_ALIVE(VisitForValue(expr->value())); + HValue* context = BuildContextChainWalk(var); + HStoreContextSlot* instr = + new(zone()) HStoreContextSlot(context, var->index(), Top()); + AddInstruction(instr); + if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + return ast_context()->ReturnValue(Pop()); + } - } else { - return Bailout("assignment to LOOKUP or const CONTEXT variable"); + case Variable::LOOKUP: + return Bailout("assignment to LOOKUP variable"); } - - } else if (prop != NULL) { - HandlePropertyAssignment(expr); } else { return Bailout("invalid left-hand side in assignment"); } @@ -3905,35 +3906,35 @@ HInstruction* HGraphBuilder::BuildExternalArrayElementAccess( HValue* external_elements, HValue* checked_key, HValue* val, - JSObject::ElementsKind elements_kind, + ElementsKind elements_kind, bool is_store) { if (is_store) { ASSERT(val != NULL); switch (elements_kind) { - case JSObject::EXTERNAL_PIXEL_ELEMENTS: { + case EXTERNAL_PIXEL_ELEMENTS: { HClampToUint8* clamp = new(zone()) HClampToUint8(val); AddInstruction(clamp); val = clamp; break; } - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: { + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: { HToInt32* floor_val = new(zone()) HToInt32(val); AddInstruction(floor_val); val = floor_val; break; } - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: break; - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -4017,7 +4018,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, SmallMapList* maps = prop->GetReceiverTypes(); bool todo_external_array = false; - static const int kNumElementTypes = JSObject::kElementsKindCount; + static const int kNumElementTypes = kElementsKindCount; bool type_todo[kNumElementTypes]; for (int i = 0; i < kNumElementTypes; ++i) { type_todo[i] = false; @@ -4027,7 +4028,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, ASSERT(maps->at(i)->IsMap()); type_todo[maps->at(i)->elements_kind()] = true; if (maps->at(i)->elements_kind() - >= JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) { + >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND) { todo_external_array = true; } } @@ -4042,16 +4043,16 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, HInstruction* checked_key = NULL; // FAST_ELEMENTS is assumed to be the first case. - STATIC_ASSERT(JSObject::FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 0); - for (JSObject::ElementsKind elements_kind = JSObject::FAST_ELEMENTS; - elements_kind <= JSObject::LAST_ELEMENTS_KIND; - elements_kind = JSObject::ElementsKind(elements_kind + 1)) { + for (ElementsKind elements_kind = FAST_ELEMENTS; + elements_kind <= LAST_ELEMENTS_KIND; + elements_kind = ElementsKind(elements_kind + 1)) { // After having handled FAST_ELEMENTS and DICTIONARY_ELEMENTS, we // need to add some code that's executed for all external array cases. - STATIC_ASSERT(JSObject::LAST_EXTERNAL_ARRAY_ELEMENTS_KIND == - JSObject::LAST_ELEMENTS_KIND); - if (elements_kind == JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND + STATIC_ASSERT(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND == + LAST_ELEMENTS_KIND); + if (elements_kind == FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND && todo_external_array) { HInstruction* length = AddInstruction(new(zone()) HFixedArrayBaseLength(elements)); @@ -4070,11 +4071,11 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, set_current_block(if_true); HInstruction* access; - if (elements_kind == JSObject::FAST_ELEMENTS || - elements_kind == JSObject::FAST_DOUBLE_ELEMENTS) { + if (elements_kind == FAST_ELEMENTS || + elements_kind == FAST_DOUBLE_ELEMENTS) { bool fast_double_elements = - elements_kind == JSObject::FAST_DOUBLE_ELEMENTS; - if (is_store && elements_kind == JSObject::FAST_ELEMENTS) { + elements_kind == FAST_DOUBLE_ELEMENTS; + if (is_store && elements_kind == FAST_ELEMENTS) { AddInstruction(new(zone()) HCheckMap( elements, isolate()->factory()->fixed_array_map(), elements_kind_branch)); @@ -4139,7 +4140,7 @@ HValue* HGraphBuilder::HandlePolymorphicElementAccess(HValue* object, new(zone()) HLoadKeyedFastElement(elements, checked_key)); } } - } else if (elements_kind == JSObject::DICTIONARY_ELEMENTS) { + } else if (elements_kind == DICTIONARY_ELEMENTS) { if (is_store) { access = AddInstruction(BuildStoreKeyedGeneric(object, key, val)); } else { @@ -4434,8 +4435,10 @@ void HGraphBuilder::TraceInline(Handle<JSFunction> target, Handle<JSFunction> caller, const char* reason) { if (FLAG_trace_inlining) { - SmartPointer<char> target_name = target->shared()->DebugName()->ToCString(); - SmartPointer<char> caller_name = caller->shared()->DebugName()->ToCString(); + SmartArrayPointer<char> target_name = + target->shared()->DebugName()->ToCString(); + SmartArrayPointer<char> caller_name = + caller->shared()->DebugName()->ToCString(); if (reason == NULL) { PrintF("Inlined %s called from %s.\n", *target_name, *caller_name); } else { @@ -4922,10 +4925,12 @@ void HGraphBuilder::VisitCall(Call* expr) { } } else { - Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); - bool global_call = (var != NULL) && var->is_global() && !var->is_this(); + VariableProxy* proxy = expr->expression()->AsVariableProxy(); + // FIXME. + bool global_call = proxy != NULL && proxy->var()->IsUnallocated(); if (global_call) { + Variable* var = proxy->var(); bool known_global_function = false; // If there is a global property cell for the name at compile time and // access check is not enabled we assume that the function will not change @@ -5089,20 +5094,8 @@ void HGraphBuilder::VisitUnaryOperation(UnaryOperation* expr) { void HGraphBuilder::VisitDelete(UnaryOperation* expr) { Property* prop = expr->expression()->AsProperty(); - Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); - if (prop == NULL && var == NULL) { - // Result of deleting non-property, non-variable reference is true. - // Evaluate the subexpression for side effects. - CHECK_ALIVE(VisitForEffect(expr->expression())); - return ast_context()->ReturnValue(graph()->GetConstantTrue()); - } else if (var != NULL && - !var->is_global() && - var->AsSlot() != NULL && - var->AsSlot()->type() != Slot::LOOKUP) { - // Result of deleting non-global, non-dynamic variables is false. - // The subexpression does not have side effects. - return ast_context()->ReturnValue(graph()->GetConstantFalse()); - } else if (prop != NULL) { + VariableProxy* proxy = expr->expression()->AsVariableProxy(); + if (prop != NULL) { CHECK_ALIVE(VisitForValue(prop->obj())); CHECK_ALIVE(VisitForValue(prop->key())); HValue* key = Pop(); @@ -5110,10 +5103,26 @@ void HGraphBuilder::VisitDelete(UnaryOperation* expr) { HValue* context = environment()->LookupContext(); HDeleteProperty* instr = new(zone()) HDeleteProperty(context, obj, key); return ast_context()->ReturnInstruction(instr, expr->id()); - } else if (var->is_global()) { - Bailout("delete with global variable"); + } else if (proxy != NULL) { + Variable* var = proxy->var(); + if (var->IsUnallocated()) { + Bailout("delete with global variable"); + } else if (var->IsStackAllocated() || var->IsContextSlot()) { + // Result of deleting non-global variables is false. 'this' is not + // really a variable, though we implement it as one. The + // subexpression does not have side effects. + HValue* value = var->is_this() + ? graph()->GetConstantTrue() + : graph()->GetConstantFalse(); + return ast_context()->ReturnValue(value); + } else { + Bailout("delete with non-global variable"); + } } else { - Bailout("delete with non-global variable"); + // Result of deleting non-property, non-variable reference is true. + // Evaluate the subexpression for side effects. + CHECK_ALIVE(VisitForEffect(expr->expression())); + return ast_context()->ReturnValue(graph()->GetConstantTrue()); } } @@ -5260,9 +5269,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { ASSERT(current_block()->HasPredecessor()); Expression* target = expr->expression(); VariableProxy* proxy = target->AsVariableProxy(); - Variable* var = proxy->AsVariable(); Property* prop = target->AsProperty(); - if (var == NULL && prop == NULL) { + if (proxy == NULL && prop == NULL) { return Bailout("invalid lhs in count operation"); } @@ -5274,7 +5282,8 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { HValue* input = NULL; // ToNumber(original_input). HValue* after = NULL; // The result after incrementing or decrementing. - if (var != NULL) { + if (proxy != NULL) { + Variable* var = proxy->var(); if (var->mode() == Variable::CONST) { return Bailout("unsupported count operation with const"); } @@ -5286,36 +5295,45 @@ void HGraphBuilder::VisitCountOperation(CountOperation* expr) { input = returns_original_input ? Top() : Pop(); Push(after); - if (var->is_global()) { - HandleGlobalVariableAssignment(var, - after, - expr->position(), - expr->AssignmentId()); - } else if (var->IsStackAllocated()) { - Bind(var, after); - } else if (var->IsContextSlot()) { - // Bail out if we try to mutate a parameter value in a function using - // the arguments object. We do not (yet) correctly handle the - // arguments property of the function. - if (info()->scope()->arguments() != NULL) { - // Parameters will rewrite to context slots. We have no direct way - // to detect that the variable is a parameter. - int count = info()->scope()->num_parameters(); - for (int i = 0; i < count; ++i) { - if (var == info()->scope()->parameter(i)) { - Bailout("assignment to parameter, function uses arguments object"); + switch (var->location()) { + case Variable::UNALLOCATED: + HandleGlobalVariableAssignment(var, + after, + expr->position(), + expr->AssignmentId()); + break; + + case Variable::PARAMETER: + case Variable::LOCAL: + Bind(var, after); + break; + + case Variable::CONTEXT: { + // Bail out if we try to mutate a parameter value in a function + // using the arguments object. We do not (yet) correctly handle the + // arguments property of the function. + if (info()->scope()->arguments() != NULL) { + // Parameters will rewrite to context slots. We have no direct + // way to detect that the variable is a parameter so we use a + // linear search of the parameter list. + int count = info()->scope()->num_parameters(); + for (int i = 0; i < count; ++i) { + if (var == info()->scope()->parameter(i)) { + return Bailout("assignment to parameter in arguments object"); + } } } + + HValue* context = BuildContextChainWalk(var); + HStoreContextSlot* instr = + new(zone()) HStoreContextSlot(context, var->index(), after); + AddInstruction(instr); + if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); + break; } - HValue* context = BuildContextChainWalk(var); - int index = var->AsSlot()->index(); - HStoreContextSlot* instr = - new(zone()) HStoreContextSlot(context, index, after); - AddInstruction(instr); - if (instr->HasSideEffects()) AddSimulate(expr->AssignmentId()); - } else { - return Bailout("lookup variable in count operation"); + case Variable::LOOKUP: + return Bailout("lookup variable in count operation"); } } else { @@ -5727,12 +5745,12 @@ void HGraphBuilder::VisitCompareOperation(CompareOperation* expr) { // residing in new space. If it is we assume that the function will stay the // same. Handle<JSFunction> target = Handle<JSFunction>::null(); - Variable* var = expr->right()->AsVariableProxy()->AsVariable(); - bool global_function = (var != NULL) && var->is_global() && !var->is_this(); + VariableProxy* proxy = expr->right()->AsVariableProxy(); + bool global_function = (proxy != NULL) && proxy->var()->IsUnallocated(); if (global_function && info()->has_global_object() && !info()->global_object()->IsAccessCheckNeeded()) { - Handle<String> name = var->name(); + Handle<String> name = proxy->name(); Handle<GlobalObject> global(info()->global_object()); LookupResult lookup; global->Lookup(*name, &lookup); @@ -5831,17 +5849,42 @@ void HGraphBuilder::VisitThisFunction(ThisFunction* expr) { void HGraphBuilder::VisitDeclaration(Declaration* decl) { - // We support only declarations that do not require code generation. - Variable* var = decl->proxy()->var(); - if (!var->IsStackAllocated() || - decl->fun() != NULL || - decl->mode() == Variable::LET) { - return Bailout("unsupported declaration"); - } - - if (decl->mode() == Variable::CONST) { - ASSERT(var->IsStackAllocated()); - environment()->Bind(var, graph()->GetConstantHole()); + HandleDeclaration(decl->proxy(), decl->mode(), decl->fun()); +} + + +void HGraphBuilder::HandleDeclaration(VariableProxy* proxy, + Variable::Mode mode, + FunctionLiteral* function) { + if (mode == Variable::LET) return Bailout("unsupported let declaration"); + Variable* var = proxy->var(); + switch (var->location()) { + case Variable::UNALLOCATED: + return Bailout("unsupported global declaration"); + case Variable::PARAMETER: + case Variable::LOCAL: + case Variable::CONTEXT: + if (mode == Variable::CONST || function != NULL) { + HValue* value = NULL; + if (mode == Variable::CONST) { + value = graph()->GetConstantHole(); + } else { + VisitForValue(function); + value = Pop(); + } + if (var->IsContextSlot()) { + HValue* context = environment()->LookupContext(); + HStoreContextSlot* store = + new HStoreContextSlot(context, var->index(), value); + AddInstruction(store); + if (store->HasSideEffects()) AddSimulate(proxy->id()); + } else { + environment()->Bind(var, value); + } + } + break; + case Variable::LOOKUP: + return Bailout("unsupported lookup slot in declaration"); } } @@ -5874,7 +5917,9 @@ void HGraphBuilder::GenerateIsFunction(CallRuntime* call) { 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()); } @@ -6528,6 +6573,8 @@ void HTracer::Trace(const char* name, HGraph* graph, LChunk* chunk) { PrintBlockProperty("dominator", current->dominator()->block_id()); } + PrintIntProperty("loop_depth", current->LoopNestingDepth()); + if (chunk != NULL) { int first_index = current->first_instruction_index(); int last_index = current->last_instruction_index(); diff --git a/src/hydrogen.h b/src/hydrogen.h index 1066d2cb..03fbc732 100644 --- a/src/hydrogen.h +++ b/src/hydrogen.h @@ -102,6 +102,7 @@ class HBasicBlock: public ZoneObject { void RemovePhi(HPhi* phi); void AddInstruction(HInstruction* instr); bool Dominates(HBasicBlock* other) const; + int LoopNestingDepth() const; void SetInitialEnvironment(HEnvironment* env); void ClearEnvironment() { last_environment_ = NULL; } @@ -242,13 +243,11 @@ class HGraph: public ZoneObject { // Returns false if there are phi-uses of the arguments-object // which are not supported by the optimizing compiler. - bool CheckArgumentsPhiUses(); + bool CheckPhis(); - // Returns false if there are phi-uses of an uninitialized const - // which are not supported by the optimizing compiler. - bool CheckConstPhiUses(); - - void CollectPhis(); + // Returns false if there are phi-uses of hole values comming + // from uninitialized consts. + bool CollectPhis(); Handle<Code> Compile(CompilationInfo* info); @@ -457,12 +456,11 @@ class HEnvironment: public ZoneObject { // by 1 (receiver is parameter index -1 but environment index 0). // Stack-allocated local indices are shifted by the number of parameters. int IndexFor(Variable* variable) const { - Slot* slot = variable->AsSlot(); - ASSERT(slot != NULL && slot->IsStackAllocated()); - int shift = (slot->type() == Slot::PARAMETER) + ASSERT(variable->IsStackAllocated()); + int shift = variable->IsParameter() ? 1 : parameter_count_ + specials_count_; - return slot->index() + shift; + return variable->index() + shift; } Handle<JSFunction> closure_; @@ -781,6 +779,10 @@ class HGraphBuilder: public AstVisitor { INLINE_RUNTIME_FUNCTION_LIST(INLINE_FUNCTION_GENERATOR_DECLARATION) #undef INLINE_FUNCTION_GENERATOR_DECLARATION + void HandleDeclaration(VariableProxy* proxy, + Variable::Mode mode, + FunctionLiteral* function); + void VisitDelete(UnaryOperation* expr); void VisitVoid(UnaryOperation* expr); void VisitTypeof(UnaryOperation* expr); @@ -853,7 +855,6 @@ class HGraphBuilder: public AstVisitor { TypeInfo info, HValue* value, Representation rep); - void AssumeRepresentation(HValue* value, Representation rep); static Representation ToRepresentation(TypeInfo info); void SetupScope(Scope* scope); @@ -935,7 +936,7 @@ class HGraphBuilder: public AstVisitor { HValue* external_elements, HValue* checked_key, HValue* val, - JSObject::ElementsKind elements_kind, + ElementsKind elements_kind, bool is_store); HInstruction* BuildMonomorphicElementAccess(HValue* object, diff --git a/src/ia32/assembler-ia32.h b/src/ia32/assembler-ia32.h index c186094b..4698e3ed 100644 --- a/src/ia32/assembler-ia32.h +++ b/src/ia32/assembler-ia32.h @@ -465,6 +465,7 @@ class CpuFeatures : public AllStatic { // Enable a specified feature within a scope. class Scope BASE_EMBEDDED { #ifdef DEBUG + public: explicit Scope(CpuFeature f) { uint64_t mask = static_cast<uint64_t>(1) << f; @@ -484,10 +485,12 @@ class CpuFeatures : public AllStatic { isolate_->set_enabled_cpu_features(old_enabled_); } } + private: Isolate* isolate_; uint64_t old_enabled_; #else + public: explicit Scope(CpuFeature f) {} #endif diff --git a/src/ia32/builtins-ia32.cc b/src/ia32/builtins-ia32.cc index 845a073c..310ea3d1 100644 --- a/src/ia32/builtins-ia32.cc +++ b/src/ia32/builtins-ia32.cc @@ -590,16 +590,17 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 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 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ push(ebx); __ InvokeBuiltin(Builtins::TO_OBJECT, CALL_FUNCTION); __ mov(ebx, eax); + __ Set(edx, Immediate(0)); // restore __ pop(eax); __ SmiUntag(eax); @@ -661,14 +663,19 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ 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 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ 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_FunctionCall(MacroAssembler* masm) { 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 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ 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 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ 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 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ 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 85e74b85..1009aaf5 100644 --- a/src/ia32/code-stubs-ia32.cc +++ b/src/ia32/code-stubs-ia32.cc @@ -3551,7 +3551,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // stack overflow (on the backtrack stack) was detected in RegExp code but // haven't created the exception yet. Handle that in the runtime system. // TODO(592): Rerunning the RegExp to get the stack overflow exception. - ExternalReference pending_exception(Isolate::k_pending_exception_address, + ExternalReference pending_exception(Isolate::kPendingExceptionAddress, masm->isolate()); __ mov(edx, Operand::StaticVariable(ExternalReference::the_hole_value_location( @@ -4199,7 +4199,7 @@ void StackCheckStub::Generate(MacroAssembler* masm) { 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 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { __ 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 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // 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); } @@ -4341,7 +4358,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ j(zero, &failure_returned); ExternalReference pending_exception_address( - Isolate::k_pending_exception_address, masm->isolate()); + Isolate::kPendingExceptionAddress, masm->isolate()); // Check that there is no pending exception, otherwise we // should have returned some failure value. @@ -4482,11 +4499,11 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ push(ebx); // Save copies of the top frame descriptor on the stack. - ExternalReference c_entry_fp(Isolate::k_c_entry_fp_address, masm->isolate()); + ExternalReference c_entry_fp(Isolate::kCEntryFPAddress, masm->isolate()); __ push(Operand::StaticVariable(c_entry_fp)); // If this is the outermost JS call, set js_entry_sp value. - ExternalReference js_entry_sp(Isolate::k_js_entry_sp_address, + ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress, masm->isolate()); __ cmp(Operand::StaticVariable(js_entry_sp), Immediate(0)); __ j(not_equal, ¬_outermost_js, Label::kNear); @@ -4503,7 +4520,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Caught exception: Store result (exception) in the pending // exception field in the JSEnv and return a failure sentinel. - ExternalReference pending_exception(Isolate::k_pending_exception_address, + ExternalReference pending_exception(Isolate::kPendingExceptionAddress, masm->isolate()); __ mov(Operand::StaticVariable(pending_exception), eax); __ mov(eax, reinterpret_cast<int32_t>(Failure::Exception())); @@ -4554,7 +4571,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Restore the top frame descriptor from the stack. __ pop(Operand::StaticVariable(ExternalReference( - Isolate::k_c_entry_fp_address, + Isolate::kCEntryFPAddress, masm->isolate()))); // Restore callee-saved registers (C calling conventions). @@ -4907,7 +4924,8 @@ void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { // Check for 1-byte or 2-byte string. __ bind(&flat_string); - STATIC_ASSERT(kAsciiStringTag != 0); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ test(result_, Immediate(kStringEncodingMask)); __ j(not_zero, &ascii_string, Label::kNear); @@ -5178,8 +5196,9 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ mov(edi, FieldOperand(edx, HeapObject::kMapOffset)); __ movzx_b(edi, FieldOperand(edi, Map::kInstanceTypeOffset)); __ and_(ecx, Operand(edi)); - STATIC_ASSERT(kStringEncodingMask == kAsciiStringTag); - __ test(ecx, Immediate(kAsciiStringTag)); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ test(ecx, Immediate(kStringEncodingMask)); __ j(zero, &non_ascii); __ bind(&ascii_data); // Allocate an acsii cons string. @@ -5210,7 +5229,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ cmp(edi, kAsciiStringTag | kAsciiDataHintTag); __ j(equal, &ascii_data); // Allocate a two byte cons string. - __ AllocateConsString(ecx, edi, no_reg, &string_add_runtime); + __ AllocateTwoByteConsString(ecx, edi, no_reg, &string_add_runtime); __ jmp(&allocated); // Handle creating a flat result. First check that both strings are not @@ -5236,12 +5255,13 @@ void StringAddStub::Generate(MacroAssembler* masm) { // ebx: length of resulting flat string as a smi // edx: second string Label non_ascii_string_add_flat_result; - STATIC_ASSERT(kStringEncodingMask == kAsciiStringTag); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ mov(ecx, FieldOperand(eax, HeapObject::kMapOffset)); - __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kAsciiStringTag); + __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask); __ j(zero, &non_ascii_string_add_flat_result); __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); - __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kAsciiStringTag); + __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask); __ j(zero, &string_add_runtime); // Both strings are ascii strings. As they are short they are both flat. @@ -5281,7 +5301,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { // edx: second string __ bind(&non_ascii_string_add_flat_result); __ mov(ecx, FieldOperand(edx, HeapObject::kMapOffset)); - __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kAsciiStringTag); + __ test_b(FieldOperand(ecx, Map::kInstanceTypeOffset), kStringEncodingMask); __ j(not_zero, &string_add_runtime); // Both strings are two byte strings. As they are short they are both // flat. @@ -5759,13 +5779,14 @@ void SubStringStub::Generate(MacroAssembler* masm) { // string's encoding is wrong because we always have to recheck encoding of // the newly created string's parent anyways due to externalized strings. Label two_byte_slice, set_slice_header; - STATIC_ASSERT(kAsciiStringTag != 0); - __ test(ebx, Immediate(kAsciiStringTag)); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ test(ebx, Immediate(kStringEncodingMask)); __ j(zero, &two_byte_slice, Label::kNear); __ AllocateAsciiSlicedString(eax, ebx, no_reg, &runtime); __ jmp(&set_slice_header, Label::kNear); __ bind(&two_byte_slice); - __ AllocateSlicedString(eax, ebx, no_reg, &runtime); + __ AllocateTwoByteSlicedString(eax, ebx, no_reg, &runtime); __ bind(&set_slice_header); __ mov(FieldOperand(eax, SlicedString::kOffsetOffset), edx); __ SmiTag(ecx); diff --git a/src/ia32/full-codegen-ia32.cc b/src/ia32/full-codegen-ia32.cc index df2542e4..ca6ce6e3 100644 --- a/src/ia32/full-codegen-ia32.cc +++ b/src/ia32/full-codegen-ia32.cc @@ -41,7 +41,6 @@ namespace v8 { namespace internal { - #define __ ACCESS_MASM(masm_) @@ -192,14 +191,14 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // Copy parameters into context if necessary. int num_parameters = info->scope()->num_parameters(); for (int i = 0; i < num_parameters; i++) { - Slot* slot = scope()->parameter(i)->AsSlot(); - if (slot != NULL && slot->type() == Slot::CONTEXT) { + Variable* var = scope()->parameter(i); + if (var->IsContextSlot()) { int parameter_offset = StandardFrameConstants::kCallerSPOffset + (num_parameters - 1 - i) * kPointerSize; // Load parameter from stack. __ mov(eax, Operand(ebp, parameter_offset)); // Store it in the context. - int context_offset = Context::SlotOffset(slot->index()); + int context_offset = Context::SlotOffset(var->index()); __ mov(Operand(esi, context_offset), eax); // Update the write barrier. This clobbers all involved // registers, so we have use a third register to avoid @@ -241,7 +240,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { ArgumentsAccessStub stub(type); __ CallStub(&stub); - Move(arguments->AsSlot(), eax, ebx, edx); + SetVar(arguments, eax, ebx, edx); } if (FLAG_trace) { @@ -255,17 +254,19 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { scope()->VisitIllegalRedeclaration(this); } else { + PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS); { Comment cmnt(masm_, "[ Declarations"); // For named function expressions, declare the function name as a // constant. if (scope()->is_function_scope() && scope()->function() != NULL) { - EmitDeclaration(scope()->function(), Variable::CONST, NULL); + int ignored = 0; + EmitDeclaration(scope()->function(), Variable::CONST, NULL, &ignored); } VisitDeclarations(scope()->declarations()); } { Comment cmnt(masm_, "[ Stack check"); - PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS); + PrepareForBailoutForId(AstNode::kDeclarationsId, NO_REGISTERS); Label ok; ExternalReference stack_limit = ExternalReference::address_of_stack_limit(isolate()); @@ -371,27 +372,29 @@ void FullCodeGenerator::verify_stack_height() { } -void FullCodeGenerator::EffectContext::Plug(Slot* slot) const { +void FullCodeGenerator::EffectContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); } -void FullCodeGenerator::AccumulatorValueContext::Plug(Slot* slot) const { - MemOperand slot_operand = codegen()->EmitSlotSearch(slot, result_register()); - __ mov(result_register(), slot_operand); +void FullCodeGenerator::AccumulatorValueContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + codegen()->GetVar(result_register(), var); } -void FullCodeGenerator::StackValueContext::Plug(Slot* slot) const { - MemOperand slot_operand = codegen()->EmitSlotSearch(slot, result_register()); +void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + MemOperand operand = codegen()->VarOperand(var, result_register()); // Memory operands can be pushed directly. - __ push(slot_operand); + __ push(operand); codegen()->increment_stack_height(); } -void FullCodeGenerator::TestContext::Plug(Slot* slot) const { +void FullCodeGenerator::TestContext::Plug(Variable* var) const { // For simplicity we always test the accumulator register. - codegen()->Move(result_register(), slot); + codegen()->GetVar(result_register(), var); codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); codegen()->DoTest(this); } @@ -615,44 +618,54 @@ void FullCodeGenerator::Split(Condition cc, } -MemOperand FullCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) { - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: - return Operand(ebp, SlotOffset(slot)); - case Slot::CONTEXT: { - int context_chain_length = - scope()->ContextChainLength(slot->var()->scope()); - __ LoadContext(scratch, context_chain_length); - return ContextOperand(scratch, slot->index()); - } - case Slot::LOOKUP: - UNREACHABLE(); +MemOperand FullCodeGenerator::StackOperand(Variable* var) { + ASSERT(var->IsStackAllocated()); + // Offset is negative because higher indexes are at lower addresses. + int offset = -var->index() * kPointerSize; + // Adjust by a (parameter or local) base offset. + if (var->IsParameter()) { + offset += (info_->scope()->num_parameters() + 1) * kPointerSize; + } else { + offset += JavaScriptFrameConstants::kLocal0Offset; } - UNREACHABLE(); - return Operand(eax, 0); + return Operand(ebp, offset); } -void FullCodeGenerator::Move(Register destination, Slot* source) { - MemOperand location = EmitSlotSearch(source, destination); - __ mov(destination, location); +MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) { + ASSERT(var->IsContextSlot() || var->IsStackAllocated()); + if (var->IsContextSlot()) { + int context_chain_length = scope()->ContextChainLength(var->scope()); + __ LoadContext(scratch, context_chain_length); + return ContextOperand(scratch, var->index()); + } else { + return StackOperand(var); + } } -void FullCodeGenerator::Move(Slot* dst, - Register src, - Register scratch1, - Register scratch2) { - ASSERT(dst->type() != Slot::LOOKUP); // Not yet implemented. - ASSERT(!scratch1.is(src) && !scratch2.is(src)); - MemOperand location = EmitSlotSearch(dst, scratch1); +void FullCodeGenerator::GetVar(Register dest, Variable* var) { + ASSERT(var->IsContextSlot() || var->IsStackAllocated()); + MemOperand location = VarOperand(var, dest); + __ mov(dest, location); +} + + +void FullCodeGenerator::SetVar(Variable* var, + Register src, + Register scratch0, + Register scratch1) { + ASSERT(var->IsContextSlot() || var->IsStackAllocated()); + ASSERT(!scratch0.is(src)); + ASSERT(!scratch0.is(scratch1)); + ASSERT(!scratch1.is(src)); + MemOperand location = VarOperand(var, scratch0); __ mov(location, src); // Emit the write barrier code if the location is in the heap. - if (dst->type() == Slot::CONTEXT) { - int offset = Context::SlotOffset(dst->index()); - ASSERT(!scratch1.is(esi) && !src.is(esi) && !scratch2.is(esi)); - __ RecordWrite(scratch1, offset, src, scratch2); + if (var->IsContextSlot()) { + int offset = Context::SlotOffset(var->index()); + ASSERT(!scratch0.is(esi) && !src.is(esi) && !scratch1.is(esi)); + __ RecordWrite(scratch0, offset, src, scratch1); } } @@ -683,29 +696,33 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, } -void FullCodeGenerator::EmitDeclaration(Variable* variable, +void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Variable::Mode mode, - FunctionLiteral* function) { - Comment cmnt(masm_, "[ Declaration"); - ASSERT(variable != NULL); // Must have been resolved. - Slot* slot = variable->AsSlot(); - ASSERT(slot != NULL); - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: + FunctionLiteral* function, + int* global_count) { + // If it was not possible to allocate the variable at compile time, we + // need to "declare" it at runtime to make sure it actually exists in the + // local context. + Variable* variable = proxy->var(); + switch (variable->location()) { + case Variable::UNALLOCATED: + ++(*global_count); + break; + + case Variable::PARAMETER: + case Variable::LOCAL: if (function != NULL) { + Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); - __ mov(Operand(ebp, SlotOffset(slot)), result_register()); + __ mov(StackOperand(variable), result_register()); } else if (mode == Variable::CONST || mode == Variable::LET) { - __ mov(Operand(ebp, SlotOffset(slot)), + Comment cmnt(masm_, "[ Declaration"); + __ mov(StackOperand(variable), Immediate(isolate()->factory()->the_hole_value())); } break; - case Slot::CONTEXT: - // We bypass the general EmitSlotSearch because we know more about - // this specific context. - + case Variable::CONTEXT: // The variable in the decl always resides in the current function // context. ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); @@ -718,22 +735,27 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, __ Check(not_equal, "Declaration in catch context."); } if (function != NULL) { + Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); - __ mov(ContextOperand(esi, slot->index()), result_register()); - int offset = Context::SlotOffset(slot->index()); + __ mov(ContextOperand(esi, variable->index()), result_register()); + int offset = Context::SlotOffset(variable->index()); __ mov(ebx, esi); __ RecordWrite(ebx, offset, result_register(), ecx); + PrepareForBailoutForId(proxy->id(), NO_REGISTERS); } else if (mode == Variable::CONST || mode == Variable::LET) { - __ mov(ContextOperand(esi, slot->index()), + Comment cmnt(masm_, "[ Declaration"); + __ mov(ContextOperand(esi, variable->index()), Immediate(isolate()->factory()->the_hole_value())); // No write barrier since the hole value is in old space. + PrepareForBailoutForId(proxy->id(), NO_REGISTERS); } break; - case Slot::LOOKUP: { + case Variable::LOOKUP: { + Comment cmnt(masm_, "[ Declaration"); __ push(esi); __ push(Immediate(variable->name())); - // Declaration nodes are always introduced in one of two modes. + // Declaration nodes are always introduced in one of three modes. ASSERT(mode == Variable::VAR || mode == Variable::CONST || mode == Variable::LET); @@ -750,7 +772,7 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, __ push(Immediate(isolate()->factory()->the_hole_value())); increment_stack_height(); } else { - __ push(Immediate(Smi::FromInt(0))); // No initial value! + __ push(Immediate(Smi::FromInt(0))); // Indicates no initial value. increment_stack_height(); } __ CallRuntime(Runtime::kDeclareContextSlot, 4); @@ -761,18 +783,15 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, } -void FullCodeGenerator::VisitDeclaration(Declaration* decl) { - EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); -} +void FullCodeGenerator::VisitDeclaration(Declaration* decl) { } void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { // Call the runtime to declare the globals. __ push(esi); // The context is the first argument. __ push(Immediate(pairs)); - __ push(Immediate(Smi::FromInt(is_eval() ? 1 : 0))); - __ push(Immediate(Smi::FromInt(strict_mode_flag()))); - __ CallRuntime(Runtime::kDeclareGlobals, 4); + __ push(Immediate(Smi::FromInt(DeclareGlobalsFlags()))); + __ CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. } @@ -1071,10 +1090,9 @@ void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { } -void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( - Slot* slot, - TypeofState typeof_state, - Label* slow) { +void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, + TypeofState typeof_state, + Label* slow) { Register context = esi; Register temp = edx; @@ -1123,7 +1141,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( // All extension objects were empty and it is safe to use a global // load IC call. __ mov(eax, GlobalObjectOperand()); - __ mov(ecx, slot->var()->name()); + __ mov(ecx, var->name()); Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) ? RelocInfo::CODE_TARGET @@ -1132,14 +1150,13 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( } -MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( - Slot* slot, - Label* slow) { - ASSERT(slot->type() == Slot::CONTEXT); +MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, + Label* slow) { + ASSERT(var->IsContextSlot()); Register context = esi; Register temp = ebx; - for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { + for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { if (s->num_heap_slots() > 0) { if (s->calls_eval()) { // Check that extension is NULL. @@ -1159,60 +1176,31 @@ MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( // This function is used only for loads, not stores, so it's safe to // return an esi-based operand (the write barrier cannot be allowed to // destroy the esi register). - return ContextOperand(context, slot->index()); + return ContextOperand(context, var->index()); } -void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( - Slot* slot, - TypeofState typeof_state, - Label* slow, - Label* done) { +void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var, + TypeofState typeof_state, + Label* slow, + Label* done) { // Generate fast-case code for variables that might be shadowed by // eval-introduced variables. Eval is used a lot without // introducing variables. In those cases, we do not want to // perform a runtime call for all variables in the scope // containing the eval. - if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) { - EmitLoadGlobalSlotCheckExtensions(slot, typeof_state, slow); + if (var->mode() == Variable::DYNAMIC_GLOBAL) { + EmitLoadGlobalCheckExtensions(var, typeof_state, slow); __ jmp(done); - } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { - Slot* potential_slot = slot->var()->local_if_not_shadowed()->AsSlot(); - Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite(); - if (potential_slot != NULL) { - // Generate fast case for locals that rewrite to slots. - __ mov(eax, - ContextSlotOperandCheckExtensions(potential_slot, slow)); - if (potential_slot->var()->mode() == Variable::CONST) { - __ cmp(eax, isolate()->factory()->the_hole_value()); - __ j(not_equal, done); - __ mov(eax, isolate()->factory()->undefined_value()); - } - __ jmp(done); - } else if (rewrite != NULL) { - // Generate fast case for calls of an argument function. - Property* property = rewrite->AsProperty(); - if (property != NULL) { - VariableProxy* obj_proxy = property->obj()->AsVariableProxy(); - Literal* key_literal = property->key()->AsLiteral(); - if (obj_proxy != NULL && - key_literal != NULL && - obj_proxy->IsArguments() && - key_literal->handle()->IsSmi()) { - // Load arguments object if there are no eval-introduced - // variables. Then load the argument from the arguments - // object using keyed load. - __ mov(edx, - ContextSlotOperandCheckExtensions(obj_proxy->var()->AsSlot(), - slow)); - __ SafeSet(eax, Immediate(key_literal->handle())); - Handle<Code> ic = - isolate()->builtins()->KeyedLoadIC_Initialize(); - __ call(ic, RelocInfo::CODE_TARGET, GetPropertyId(property)); - __ jmp(done); - } - } + } else if (var->mode() == Variable::DYNAMIC_LOCAL) { + Variable* local = var->local_if_not_shadowed(); + __ mov(eax, ContextSlotOperandCheckExtensions(local, slow)); + if (local->mode() == Variable::CONST) { + __ cmp(eax, isolate()->factory()->the_hole_value()); + __ j(not_equal, done); + __ mov(eax, isolate()->factory()->undefined_value()); } + __ jmp(done); } } @@ -1222,66 +1210,60 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { SetSourcePosition(proxy->position()); Variable* var = proxy->var(); - // Three cases: non-this global variables, lookup slots, and all other - // types of slots. - Slot* slot = var->AsSlot(); - ASSERT((var->is_global() && !var->is_this()) == (slot == NULL)); - - if (slot == NULL) { - Comment cmnt(masm_, "Global variable"); - // Use inline caching. Variable name is passed in ecx and the global - // object on the stack. - __ mov(eax, GlobalObjectOperand()); - __ mov(ecx, var->name()); - Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); - __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); - context()->Plug(eax); - - } else if (slot->type() == Slot::LOOKUP) { - Label done, slow; - - // Generate code for loading from variables potentially shadowed - // by eval-introduced variables. - EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done); - - __ bind(&slow); - Comment cmnt(masm_, "Lookup slot"); - __ push(esi); // Context. - __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kLoadContextSlot, 2); - __ bind(&done); + // Three cases: global variables, lookup variables, and all other types of + // variables. + switch (var->location()) { + case Variable::UNALLOCATED: { + Comment cmnt(masm_, "Global variable"); + // Use inline caching. Variable name is passed in ecx and the global + // object in eax. + __ mov(eax, GlobalObjectOperand()); + __ mov(ecx, var->name()); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); + __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); + context()->Plug(eax); + break; + } - context()->Plug(eax); + case Variable::PARAMETER: + case Variable::LOCAL: + case Variable::CONTEXT: { + Comment cmnt(masm_, var->IsContextSlot() + ? "Context variable" + : "Stack variable"); + if (var->mode() != Variable::LET && var->mode() != Variable::CONST) { + context()->Plug(var); + } else { + // Let and const need a read barrier. + Label done; + GetVar(eax, var); + __ cmp(eax, isolate()->factory()->the_hole_value()); + __ j(not_equal, &done, Label::kNear); + if (var->mode() == Variable::LET) { + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + } else { // Variable::CONST + __ mov(eax, isolate()->factory()->undefined_value()); + } + __ bind(&done); + context()->Plug(eax); + } + break; + } - } else { - Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) - ? "Context slot" - : "Stack slot"); - if (var->mode() == Variable::CONST) { - // Constants may be the hole value if they have not been initialized. - // Unhole them. - Label done; - MemOperand slot_operand = EmitSlotSearch(slot, eax); - __ mov(eax, slot_operand); - __ cmp(eax, isolate()->factory()->the_hole_value()); - __ j(not_equal, &done, Label::kNear); - __ mov(eax, isolate()->factory()->undefined_value()); - __ bind(&done); - context()->Plug(eax); - } else if (var->mode() == Variable::LET) { - // Let bindings may be the hole value if they have not been initialized. - // Throw a type error in this case. - Label done; - MemOperand slot_operand = EmitSlotSearch(slot, eax); - __ mov(eax, slot_operand); - __ cmp(eax, isolate()->factory()->the_hole_value()); - __ j(not_equal, &done, Label::kNear); + case Variable::LOOKUP: { + Label done, slow; + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done); + __ bind(&slow); + Comment cmnt(masm_, "Lookup variable"); + __ push(esi); // Context. __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ CallRuntime(Runtime::kLoadContextSlot, 2); __ bind(&done); context()->Plug(eax); - } else { - context()->Plug(slot); + break; } } } @@ -1824,14 +1806,8 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) { - ASSERT(var != NULL); - ASSERT(var->is_global() || var->AsSlot() != NULL); - - if (var->is_global()) { - ASSERT(!var->is_this()); - // Assignment to a global variable. Use inline caching for the - // assignment. Right-hand-side value is passed in eax, variable name in - // ecx, and the global object on the stack. + if (var->IsUnallocated()) { + // Global var, const, or let. __ mov(ecx, var->name()); __ mov(edx, GlobalObjectOperand()); Handle<Code> ic = is_strict_mode() @@ -1840,117 +1816,79 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); } else if (op == Token::INIT_CONST) { - // Like var declarations, const declarations are hoisted to function - // scope. However, unlike var initializers, const initializers are able - // to drill a hole to that function context, even from inside a 'with' - // context. We thus bypass the normal static scope lookup. - Slot* slot = var->AsSlot(); - Label skip; - switch (slot->type()) { - case Slot::PARAMETER: - // No const parameters. - UNREACHABLE(); - break; - case Slot::LOCAL: - __ mov(edx, Operand(ebp, SlotOffset(slot))); - __ cmp(edx, isolate()->factory()->the_hole_value()); - __ j(not_equal, &skip); - __ mov(Operand(ebp, SlotOffset(slot)), eax); - break; - case Slot::CONTEXT: - case Slot::LOOKUP: - __ push(eax); - __ push(esi); - __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); - break; + // Const initializers need a write barrier. + ASSERT(!var->IsParameter()); // No const parameters. + if (var->IsStackLocal()) { + Label skip; + __ mov(edx, StackOperand(var)); + __ cmp(edx, isolate()->factory()->the_hole_value()); + __ j(not_equal, &skip); + __ mov(StackOperand(var), eax); + __ bind(&skip); + } else { + ASSERT(var->IsContextSlot() || var->IsLookupSlot()); + // Like var declarations, const declarations are hoisted to function + // scope. However, unlike var initializers, const initializers are + // able to drill a hole to that function context, even from inside a + // 'with' context. We thus bypass the normal static scope lookup for + // var->IsContextSlot(). + __ push(eax); + __ push(esi); + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); } - __ bind(&skip); } else if (var->mode() == Variable::LET && op != Token::INIT_LET) { - // Perform the assignment for non-const variables. Const assignments - // are simply skipped. - Slot* slot = var->AsSlot(); - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: { - Label assign; - // Check for an initialized let binding. - __ mov(edx, Operand(ebp, SlotOffset(slot))); - __ cmp(edx, isolate()->factory()->the_hole_value()); - __ j(not_equal, &assign); - __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - // Perform the assignment. - __ bind(&assign); - __ mov(Operand(ebp, SlotOffset(slot)), eax); - break; - } - - case Slot::CONTEXT: { - // Let variables may be the hole value if they have not been - // initialized. Throw a type error in this case. - Label assign; - MemOperand target = EmitSlotSearch(slot, ecx); - // Check for an initialized let binding. - __ mov(edx, target); - __ cmp(edx, isolate()->factory()->the_hole_value()); - __ j(not_equal, &assign, Label::kNear); - __ push(Immediate(var->name())); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - // Perform the assignment. - __ bind(&assign); - __ mov(target, eax); - // The value of the assignment is in eax. RecordWrite clobbers its - // register arguments. + // Non-initializing assignment to let variable needs a write barrier. + if (var->IsLookupSlot()) { + __ push(eax); // Value. + __ push(esi); // Context. + __ push(Immediate(var->name())); + __ push(Immediate(Smi::FromInt(strict_mode_flag()))); + __ CallRuntime(Runtime::kStoreContextSlot, 4); + } else { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + Label assign; + MemOperand location = VarOperand(var, ecx); + __ mov(edx, location); + __ cmp(edx, isolate()->factory()->the_hole_value()); + __ j(not_equal, &assign, Label::kNear); + __ push(Immediate(var->name())); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ bind(&assign); + __ mov(location, eax); + if (var->IsContextSlot()) { __ mov(edx, eax); - int offset = Context::SlotOffset(slot->index()); - __ RecordWrite(ecx, offset, edx, ebx); - break; + __ RecordWrite(ecx, Context::SlotOffset(var->index()), edx, ebx); } - - case Slot::LOOKUP: - // Call the runtime for the assignment. - __ push(eax); // Value. - __ push(esi); // Context. - __ push(Immediate(var->name())); - __ push(Immediate(Smi::FromInt(strict_mode_flag()))); - __ CallRuntime(Runtime::kStoreContextSlot, 4); - break; } - } else if (var->mode() != Variable::CONST) { - // Perform the assignment for non-const variables. Const assignments - // are simply skipped. - Slot* slot = var->AsSlot(); - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: - // Perform the assignment. - __ mov(Operand(ebp, SlotOffset(slot)), eax); - break; - case Slot::CONTEXT: { - MemOperand target = EmitSlotSearch(slot, ecx); - // Perform the assignment and issue the write barrier. - __ mov(target, eax); - // The value of the assignment is in eax. RecordWrite clobbers its - // register arguments. + } else if (var->mode() != Variable::CONST) { + // Assignment to var or initializing assignment to let. + if (var->IsStackAllocated() || var->IsContextSlot()) { + MemOperand location = VarOperand(var, ecx); + if (FLAG_debug_code && op == Token::INIT_LET) { + // Check for an uninitialized let binding. + __ mov(edx, location); + __ cmp(edx, isolate()->factory()->the_hole_value()); + __ Check(equal, "Let binding re-initialization."); + } + // Perform the assignment. + __ mov(location, eax); + if (var->IsContextSlot()) { __ mov(edx, eax); - int offset = Context::SlotOffset(slot->index()); - __ RecordWrite(ecx, offset, edx, ebx); - break; + __ RecordWrite(ecx, Context::SlotOffset(var->index()), edx, ebx); } - - case Slot::LOOKUP: - // Call the runtime for the assignment. - __ push(eax); // Value. - __ push(esi); // Context. - __ push(Immediate(var->name())); - __ push(Immediate(Smi::FromInt(strict_mode_flag()))); - __ CallRuntime(Runtime::kStoreContextSlot, 4); - break; + } else { + ASSERT(var->IsLookupSlot()); + __ push(eax); // Value. + __ push(esi); // Context. + __ push(Immediate(var->name())); + __ push(Immediate(Smi::FromInt(strict_mode_flag()))); + __ CallRuntime(Runtime::kStoreContextSlot, 4); } } + // Non-initializing assignments to consts are ignored. } @@ -2075,9 +2013,8 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, } // 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. @@ -2109,9 +2046,8 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, } // 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); @@ -2133,8 +2069,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { } // 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. @@ -2157,8 +2092,13 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, // Push the receiver of the enclosing function. __ push(Operand(ebp, (2 + info_->scope()->num_parameters()) * kPointerSize)); - // Push the strict mode flag. - __ push(Immediate(Smi::FromInt(strict_mode_flag()))); + // Push the strict mode flag. In harmony mode every eval call + // is a strict mode eval call. + StrictModeFlag strict_mode = strict_mode_flag(); + if (FLAG_harmony_block_scoping) { + strict_mode = kStrictMode; + } + __ push(Immediate(Smi::FromInt(strict_mode))); __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP ? Runtime::kResolvePossiblyDirectEvalNoLookup @@ -2174,18 +2114,18 @@ void FullCodeGenerator::VisitCall(Call* expr) { #endif Comment cmnt(masm_, "[ Call"); - Expression* fun = expr->expression(); - Variable* var = fun->AsVariableProxy()->AsVariable(); + Expression* callee = expr->expression(); + VariableProxy* proxy = callee->AsVariableProxy(); + Property* property = callee->AsProperty(); - if (var != NULL && var->is_possibly_eval()) { + if (proxy != NULL && proxy->var()->is_possibly_eval()) { // In a call to eval, we first call %ResolvePossiblyDirectEval to - // resolve the function we need to call and the receiver of the - // call. Then we call the resolved function using the given - // arguments. + // resolve the function we need to call and the receiver of the call. + // Then we call the resolved function using the given arguments. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); { PreservePositionScope pos_scope(masm()->positions_recorder()); - VisitForStackValue(fun); + VisitForStackValue(callee); // Reserved receiver slot. __ push(Immediate(isolate()->factory()->undefined_value())); increment_stack_height(); @@ -2195,15 +2135,14 @@ void FullCodeGenerator::VisitCall(Call* expr) { } // If we know that eval can only be shadowed by eval-introduced - // variables we attempt to load the global eval function directly - // in generated code. If we succeed, there is no need to perform a + // variables we attempt to load the global eval function directly in + // generated code. If we succeed, there is no need to perform a // context lookup in the runtime system. Label done; - if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) { + Variable* var = proxy->var(); + if (!var->IsUnallocated() && var->mode() == Variable::DYNAMIC_GLOBAL) { Label slow; - EmitLoadGlobalSlotCheckExtensions(var->AsSlot(), - NOT_INSIDE_TYPEOF, - &slow); + EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow); // Push the function and resolve eval. __ push(eax); EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); @@ -2211,13 +2150,11 @@ void FullCodeGenerator::VisitCall(Call* expr) { __ bind(&slow); } - // Push copy of the function (found below the arguments) and + // Push a copy of the function (found below the arguments) and // resolve eval. __ push(Operand(esp, (arg_count + 1) * kPointerSize)); EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); - if (done.is_linked()) { - __ bind(&done); - } + __ bind(&done); // The runtime call returns a pair of values in eax (function) and // edx (receiver). Touch up the stack with the right values. @@ -2226,83 +2163,74 @@ void FullCodeGenerator::VisitCall(Call* expr) { } // 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. __ mov(esi, Operand(ebp, StandardFrameConstants::kContextOffset)); decrement_stack_height(arg_count + 1); // Function is left on the stack. context()->DropAndPlug(1, eax); - } else if (var != NULL && !var->is_this() && var->is_global()) { + + } else if (proxy != NULL && proxy->var()->IsUnallocated()) { // Push global object as receiver for the call IC. __ push(GlobalObjectOperand()); increment_stack_height(); - EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); - } else if (var != NULL && var->AsSlot() != NULL && - var->AsSlot()->type() == Slot::LOOKUP) { + EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT); + + } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { // Call to a lookup slot (dynamically introduced variable). Label slow, done; - { PreservePositionScope scope(masm()->positions_recorder()); - // Generate code for loading from variables potentially shadowed - // by eval-introduced variables. - EmitDynamicLoadFromSlotFastCase(var->AsSlot(), - NOT_INSIDE_TYPEOF, - &slow, - &done); + // Generate code for loading from variables potentially shadowed by + // eval-introduced variables. + EmitDynamicLookupFastCase(proxy->var(), NOT_INSIDE_TYPEOF, &slow, &done); } - __ bind(&slow); - // Call the runtime to find the function to call (returned in eax) - // and the object holding it (returned in edx). + // Call the runtime to find the function to call (returned in eax) and + // the object holding it (returned in edx). __ push(context_register()); - __ push(Immediate(var->name())); + __ push(Immediate(proxy->name())); __ CallRuntime(Runtime::kLoadContextSlot, 2); __ push(eax); // Function. - increment_stack_height(); __ push(edx); // Receiver. - increment_stack_height(); + increment_stack_height(2); - // If fast case code has been generated, emit code to push the - // function and receiver and have the slow path jump around this - // code. + // If fast case code has been generated, emit code to push the function + // and receiver and have the slow path jump around this code. if (done.is_linked()) { Label call; - __ jmp(&call); + __ jmp(&call, Label::kNear); __ bind(&done); - // Push function. Stack height already incremented in slow case above. + // Push function. Stack height already incremented in slow case + // above. __ push(eax); - // The receiver is implicitly the global receiver. Indicate this - // by passing the hole to the call function stub. + // The receiver is implicitly the global receiver. Indicate this by + // passing the hole to the call function stub. __ push(Immediate(isolate()->factory()->the_hole_value())); __ bind(&call); } - // The receiver is either the global receiver or an object found - // by LoadContextSlot. That object could be the hole if the - // receiver is implicitly the global object. + // The receiver is either the global receiver or an object found by + // LoadContextSlot. That object could be the hole if the receiver is + // implicitly the global object. EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT); - } else if (fun->AsProperty() != NULL) { - // Call to an object property. - Property* prop = fun->AsProperty(); - Literal* key = prop->key()->AsLiteral(); - if (key != NULL && key->handle()->IsSymbol()) { - // Call to a named property, use call IC. - { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(prop->obj()); - } - EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); + + } else if (property != NULL) { + { PreservePositionScope scope(masm()->positions_recorder()); + VisitForStackValue(property->obj()); + } + if (property->key()->IsPropertyName()) { + EmitCallWithIC(expr, + property->key()->AsLiteral()->handle(), + RelocInfo::CODE_TARGET); } else { - // Call to a keyed property. - { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(prop->obj()); - } - EmitKeyedCallWithIC(expr, prop->key()); + EmitKeyedCallWithIC(expr, property->key()); } + } else { + // Call to an arbitrary expression not handled specially above. { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(fun); + VisitForStackValue(callee); } // Load global receiver object. __ mov(ebx, GlobalObjectOperand()); @@ -3650,10 +3578,9 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { 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)); @@ -3674,31 +3601,32 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { switch (expr->op()) { case Token::DELETE: { Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); - Property* prop = expr->expression()->AsProperty(); - Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); + Property* property = expr->expression()->AsProperty(); + VariableProxy* proxy = expr->expression()->AsVariableProxy(); - if (prop != NULL) { - VisitForStackValue(prop->obj()); - VisitForStackValue(prop->key()); + if (property != NULL) { + VisitForStackValue(property->obj()); + VisitForStackValue(property->key()); __ push(Immediate(Smi::FromInt(strict_mode_flag()))); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); decrement_stack_height(2); context()->Plug(eax); - } else if (var != NULL) { + } else if (proxy != NULL) { + Variable* var = proxy->var(); // Delete of an unqualified identifier is disallowed in strict mode - // but "delete this" is. + // but "delete this" is allowed. ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this()); - if (var->is_global()) { + if (var->IsUnallocated()) { __ push(GlobalObjectOperand()); __ push(Immediate(var->name())); __ push(Immediate(Smi::FromInt(kNonStrictMode))); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); context()->Plug(eax); - } else if (var->AsSlot() != NULL && - var->AsSlot()->type() != Slot::LOOKUP) { - // Result of deleting non-global, non-dynamic variables is false. - // The subexpression does not have side effects. - context()->Plug(false); + } else if (var->IsStackAllocated() || var->IsContextSlot()) { + // Result of deleting non-global variables is false. 'this' is + // not really a variable, though we implement it as one. The + // subexpression does not have side effects. + context()->Plug(var->is_this()); } else { // Non-global variable. Call the runtime to try to delete from the // context where the variable was introduced. @@ -3995,7 +3923,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { ASSERT(!context()->IsEffect()); ASSERT(!context()->IsTest()); - if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { + if (proxy != NULL && proxy->var()->IsUnallocated()) { Comment cmnt(masm_, "Global variable"); __ mov(eax, GlobalObjectOperand()); __ mov(ecx, Immediate(proxy->name())); @@ -4005,15 +3933,12 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { __ call(ic); PrepareForBailout(expr, TOS_REG); context()->Plug(eax); - } else if (proxy != NULL && - proxy->var()->AsSlot() != NULL && - proxy->var()->AsSlot()->type() == Slot::LOOKUP) { + } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { Label done, slow; // Generate code for loading from variables potentially shadowed // by eval-introduced variables. - Slot* slot = proxy->var()->AsSlot(); - EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done); + EmitDynamicLookupFastCase(proxy->var(), INSIDE_TYPEOF, &slow, &done); __ bind(&slow); __ push(esi); diff --git a/src/ia32/ic-ia32.cc b/src/ia32/ic-ia32.cc index 7d3ead2e..9b5cc564 100644 --- a/src/ia32/ic-ia32.cc +++ b/src/ia32/ic-ia32.cc @@ -144,7 +144,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, StringDictionary::kElementsStartIndex * kPointerSize; const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; __ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag), - Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize)); + Immediate(PropertyDetails::TypeField::kMask << kSmiTagSize)); __ j(not_zero, miss_label); // Get the value at the masked, scaled index. @@ -198,9 +198,9 @@ static void GenerateDictionaryStore(MacroAssembler* masm, StringDictionary::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize; const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; - const int kTypeAndReadOnlyMask - = (PropertyDetails::TypeField::mask() | - PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize; + const int kTypeAndReadOnlyMask = + (PropertyDetails::TypeField::kMask | + PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize; __ test(Operand(elements, r0, times_4, kDetailsOffset - kHeapObjectTag), Immediate(kTypeAndReadOnlyMask)); __ j(not_zero, miss_label); @@ -832,7 +832,6 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Probe the stub cache. Code::Flags flags = Code::ComputeFlags(kind, - NOT_IN_LOOP, MONOMORPHIC, extra_ic_state, NORMAL, @@ -1237,9 +1236,7 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { // ----------------------------------- // 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 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm, // -- 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 32e3074d..4e3ea981 100644 --- a/src/ia32/lithium-codegen-ia32.cc +++ b/src/ia32/lithium-codegen-ia32.cc @@ -88,7 +88,8 @@ void LCodeGen::FinishCode(Handle<Code> code) { void LCodeGen::Abort(const char* format, ...) { if (FLAG_trace_bailout) { - SmartPointer<char> name(info()->shared_info()->DebugName()->ToCString()); + SmartArrayPointer<char> name( + info()->shared_info()->DebugName()->ToCString()); PrintF("Aborting LCodeGen in @\"%s\": ", *name); va_list arguments; va_start(arguments, format); @@ -194,14 +195,14 @@ bool LCodeGen::GeneratePrologue() { // Copy parameters into context if necessary. int num_parameters = scope()->num_parameters(); for (int i = 0; i < num_parameters; i++) { - Slot* slot = scope()->parameter(i)->AsSlot(); - if (slot != NULL && slot->type() == Slot::CONTEXT) { + Variable* var = scope()->parameter(i); + if (var->IsContextSlot()) { int parameter_offset = StandardFrameConstants::kCallerSPOffset + (num_parameters - 1 - i) * kPointerSize; // Load parameter from stack. __ mov(eax, Operand(ebp, parameter_offset)); // Store it in the context. - int context_offset = Context::SlotOffset(slot->index()); + int context_offset = Context::SlotOffset(var->index()); __ mov(Operand(esi, context_offset), eax); // Update the write barrier. This clobbers all involved // registers, so we have to use a third register to avoid @@ -2219,11 +2220,11 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) { __ movzx_b(temp, FieldOperand(temp, Map::kBitField2Offset)); __ and_(temp, Map::kElementsKindMask); __ shr(temp, Map::kElementsKindShift); - __ cmp(temp, JSObject::FAST_ELEMENTS); + __ cmp(temp, FAST_ELEMENTS); __ j(equal, &ok, Label::kNear); - __ cmp(temp, JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); + __ cmp(temp, FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND); __ j(less, &fail, Label::kNear); - __ cmp(temp, JSObject::LAST_EXTERNAL_ARRAY_ELEMENTS_KIND); + __ cmp(temp, LAST_EXTERNAL_ARRAY_ELEMENTS_KIND); __ j(less_equal, &ok, Label::kNear); __ bind(&fail); __ Abort("Check for fast or external elements failed."); @@ -2264,7 +2265,7 @@ void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) { // Load the result. __ mov(result, BuildFastArrayOperand(instr->elements(), instr->key(), - JSObject::FAST_ELEMENTS, + FAST_ELEMENTS, FixedArray::kHeaderSize - kHeapObjectTag)); // Check for the hole value. @@ -2284,14 +2285,14 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( sizeof(kHoleNanLower32); Operand hole_check_operand = BuildFastArrayOperand( instr->elements(), instr->key(), - JSObject::FAST_DOUBLE_ELEMENTS, + FAST_DOUBLE_ELEMENTS, offset); __ cmp(hole_check_operand, Immediate(kHoleNanUpper32)); DeoptimizeIf(equal, instr->environment()); } Operand double_load_operand = BuildFastArrayOperand( - instr->elements(), instr->key(), JSObject::FAST_DOUBLE_ELEMENTS, + instr->elements(), instr->key(), FAST_DOUBLE_ELEMENTS, FixedDoubleArray::kHeaderSize - kHeapObjectTag); __ movdbl(result, double_load_operand); } @@ -2300,7 +2301,7 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( Operand LCodeGen::BuildFastArrayOperand( LOperand* elements_pointer, LOperand* key, - JSObject::ElementsKind elements_kind, + ElementsKind elements_kind, uint32_t offset) { Register elements_pointer_reg = ToRegister(elements_pointer); int shift_size = ElementsKindToShiftSize(elements_kind); @@ -2320,35 +2321,35 @@ Operand LCodeGen::BuildFastArrayOperand( void LCodeGen::DoLoadKeyedSpecializedArrayElement( LLoadKeyedSpecializedArrayElement* instr) { - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); Operand operand(BuildFastArrayOperand(instr->external_pointer(), instr->key(), elements_kind, 0)); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { XMMRegister result(ToDoubleRegister(instr->result())); __ movss(result, operand); __ cvtss2sd(result, result); - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { __ movdbl(ToDoubleRegister(instr->result()), operand); } else { Register result(ToRegister(instr->result())); switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: __ movsx_b(result, operand); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ movzx_b(result, operand); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: __ movsx_w(result, operand); break; - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ movzx_w(result, operand); break; - case JSObject::EXTERNAL_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: __ mov(result, operand); break; - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ mov(result, operand); __ test(result, Operand(result)); // TODO(danno): we could be more clever here, perhaps having a special @@ -2356,12 +2357,12 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( // happens, and generate code that returns a double rather than int. DeoptimizeIf(negative, instr->environment()); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -2980,8 +2981,8 @@ void LCodeGen::DoCallKeyed(LCallKeyed* instr) { 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); } @@ -2993,7 +2994,7 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) { 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); } @@ -3004,7 +3005,7 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) { 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); } @@ -3017,7 +3018,7 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) { 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); } @@ -3103,36 +3104,36 @@ void LCodeGen::DoBoundsCheck(LBoundsCheck* instr) { void LCodeGen::DoStoreKeyedSpecializedArrayElement( LStoreKeyedSpecializedArrayElement* instr) { - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); Operand operand(BuildFastArrayOperand(instr->external_pointer(), instr->key(), elements_kind, 0)); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { __ cvtsd2ss(xmm0, ToDoubleRegister(instr->value())); __ movss(operand, xmm0); - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { __ movdbl(operand, ToDoubleRegister(instr->value())); } else { Register value = ToRegister(instr->value()); switch (elements_kind) { - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case JSObject::EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: __ mov_b(operand, value); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ mov_w(operand, value); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ mov(operand, value); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -3175,7 +3176,6 @@ void LCodeGen::DoStoreKeyedFastElement(LStoreKeyedFastElement* instr) { void LCodeGen::DoStoreKeyedFastDoubleElement( LStoreKeyedFastDoubleElement* instr) { XMMRegister value = ToDoubleRegister(instr->value()); - Register key = instr->key()->IsRegister() ? ToRegister(instr->key()) : no_reg; Label have_value; __ ucomisd(value, value); @@ -3187,7 +3187,7 @@ void LCodeGen::DoStoreKeyedFastDoubleElement( __ bind(&have_value); Operand double_store_operand = BuildFastArrayOperand( - instr->elements(), instr->key(), JSObject::FAST_DOUBLE_ELEMENTS, + instr->elements(), instr->key(), FAST_DOUBLE_ELEMENTS, FixedDoubleArray::kHeaderSize - kHeapObjectTag); __ movdbl(double_store_operand, value); } @@ -3269,7 +3269,8 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { // Dispatch on the encoding: ASCII or two-byte. Label ascii_string; - STATIC_ASSERT(kAsciiStringTag != 0); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ test(result, Immediate(kStringEncodingMask)); __ j(not_zero, &ascii_string, Label::kNear); diff --git a/src/ia32/lithium-codegen-ia32.h b/src/ia32/lithium-codegen-ia32.h index d26f2455..61563274 100644 --- a/src/ia32/lithium-codegen-ia32.h +++ b/src/ia32/lithium-codegen-ia32.h @@ -224,7 +224,7 @@ class LCodeGen BASE_EMBEDDED { int ToInteger32(LConstantOperand* op) const; Operand BuildFastArrayOperand(LOperand* elements_pointer, LOperand* key, - JSObject::ElementsKind elements_kind, + ElementsKind elements_kind, uint32_t offset); // Specific math operations - used from DoUnaryMathOperation. diff --git a/src/ia32/lithium-ia32.cc b/src/ia32/lithium-ia32.cc index f59ee07d..3dc220d3 100644 --- a/src/ia32/lithium-ia32.cc +++ b/src/ia32/lithium-ia32.cc @@ -315,13 +315,13 @@ void LCallKeyed::PrintDataTo(StringStream* stream) { void LCallNamed::PrintDataTo(StringStream* stream) { - SmartPointer<char> name_string = name()->ToCString(); + SmartArrayPointer<char> name_string = name()->ToCString(); stream->Add("%s #%d / ", *name_string, arity()); } void LCallGlobal::PrintDataTo(StringStream* stream) { - SmartPointer<char> name_string = name()->ToCString(); + SmartArrayPointer<char> name_string = name()->ToCString(); stream->Add("%s #%d / ", *name_string, arity()); } @@ -540,7 +540,8 @@ LChunk* LChunkBuilder::Build() { void LChunkBuilder::Abort(const char* format, ...) { if (FLAG_trace_bailout) { - SmartPointer<char> name(info()->shared_info()->DebugName()->ToCString()); + SmartArrayPointer<char> name( + info()->shared_info()->DebugName()->ToCString()); PrintF("Aborting LChunk building in @\"%s\": ", *name); va_list arguments; va_start(arguments, format); @@ -706,9 +707,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble( LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { HEnvironment* hydrogen_env = current_block_->last_environment(); - int argument_index_accumulator = 0; - instr->set_environment(CreateEnvironment(hydrogen_env, - &argument_index_accumulator)); + instr->set_environment(CreateEnvironment(hydrogen_env)); return instr; } @@ -995,13 +994,10 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { } -LEnvironment* LChunkBuilder::CreateEnvironment( - HEnvironment* hydrogen_env, - int* argument_index_accumulator) { +LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) { if (hydrogen_env == NULL) return NULL; - LEnvironment* outer = - CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); + LEnvironment* outer = CreateEnvironment(hydrogen_env->outer()); int ast_id = hydrogen_env->ast_id(); ASSERT(ast_id != AstNode::kNoNumber); int value_count = hydrogen_env->length(); @@ -1011,6 +1007,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment( argument_count_, value_count, outer); + int argument_index = 0; for (int i = 0; i < value_count; ++i) { if (hydrogen_env->is_special_index(i)) continue; @@ -1019,7 +1016,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment( if (value->IsArgumentsObject()) { op = NULL; } else if (value->IsPushArgument()) { - op = new LArgument((*argument_index_accumulator)++); + op = new LArgument(argument_index++); } else { op = UseAny(value); } @@ -1906,15 +1903,15 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastDoubleElement( LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( HLoadKeyedSpecializedArrayElement* instr) { - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); Representation representation(instr->representation()); ASSERT( (representation.IsInteger32() && - (elements_kind != JSObject::EXTERNAL_FLOAT_ELEMENTS) && - (elements_kind != JSObject::EXTERNAL_DOUBLE_ELEMENTS)) || + (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && + (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || (representation.IsDouble() && - ((elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) || - (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS)))); + ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || + (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->key()->representation().IsInteger32()); LOperand* external_pointer = UseRegister(instr->external_pointer()); LOperand* key = UseRegisterOrConstant(instr->key()); @@ -1924,7 +1921,7 @@ LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( LInstruction* load_instr = DefineAsRegister(result); // An unsigned int array load might overflow and cause a deopt, make sure it // has an environment. - return (elements_kind == JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS) + return (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) ? AssignEnvironment(load_instr) : load_instr; } @@ -1976,23 +1973,23 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastDoubleElement( LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( HStoreKeyedSpecializedArrayElement* instr) { Representation representation(instr->value()->representation()); - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); ASSERT( (representation.IsInteger32() && - (elements_kind != JSObject::EXTERNAL_FLOAT_ELEMENTS) && - (elements_kind != JSObject::EXTERNAL_DOUBLE_ELEMENTS)) || + (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && + (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || (representation.IsDouble() && - ((elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) || - (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS)))); + ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || + (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->external_pointer()->representation().IsExternal()); ASSERT(instr->key()->representation().IsInteger32()); LOperand* external_pointer = UseRegister(instr->external_pointer()); LOperand* key = UseRegisterOrConstant(instr->key()); LOperand* val = NULL; - if (elements_kind == JSObject::EXTERNAL_BYTE_ELEMENTS || - elements_kind == JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS || - elements_kind == JSObject::EXTERNAL_PIXEL_ELEMENTS) { + if (elements_kind == EXTERNAL_BYTE_ELEMENTS || + elements_kind == EXTERNAL_UNSIGNED_BYTE_ELEMENTS || + elements_kind == EXTERNAL_PIXEL_ELEMENTS) { // We need a byte register in this case for the value. val = UseFixed(instr->value(), eax); } else { diff --git a/src/ia32/lithium-ia32.h b/src/ia32/lithium-ia32.h index 4b407a32..038049ca 100644 --- a/src/ia32/lithium-ia32.h +++ b/src/ia32/lithium-ia32.h @@ -1184,7 +1184,7 @@ class LLoadKeyedSpecializedArrayElement: public LTemplateInstruction<1, 2, 0> { LOperand* external_pointer() { return inputs_[0]; } LOperand* key() { return inputs_[1]; } - JSObject::ElementsKind elements_kind() const { + ElementsKind elements_kind() const { return hydrogen()->elements_kind(); } }; @@ -1699,7 +1699,7 @@ class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> { LOperand* external_pointer() { return inputs_[0]; } LOperand* key() { return inputs_[1]; } LOperand* value() { return inputs_[2]; } - JSObject::ElementsKind elements_kind() const { + ElementsKind elements_kind() const { return hydrogen()->elements_kind(); } }; @@ -2259,8 +2259,7 @@ class LChunkBuilder BASE_EMBEDDED { LInstruction* instr, int ast_id); void ClearInstructionPendingDeoptimizationEnvironment(); - LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, - int* argument_index_accumulator); + LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env); void VisitInstruction(HInstruction* current); diff --git a/src/ia32/macro-assembler-ia32.cc b/src/ia32/macro-assembler-ia32.cc index dff174cb..837112a5 100644 --- a/src/ia32/macro-assembler-ia32.cc +++ b/src/ia32/macro-assembler-ia32.cc @@ -287,7 +287,7 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { void MacroAssembler::CheckFastElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(JSObject::FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 0); cmpb(FieldOperand(map, Map::kBitField2Offset), Map::kMaximumBitField2FastElementValue); j(above, fail, distance); @@ -437,9 +437,9 @@ void MacroAssembler::EnterExitFramePrologue() { push(Immediate(CodeObject())); // Accessed from ExitFrame::code_slot. // Save the frame pointer and the context in top. - ExternalReference c_entry_fp_address(Isolate::k_c_entry_fp_address, + ExternalReference c_entry_fp_address(Isolate::kCEntryFPAddress, isolate()); - ExternalReference context_address(Isolate::k_context_address, + ExternalReference context_address(Isolate::kContextAddress, isolate()); mov(Operand::StaticVariable(c_entry_fp_address), ebp); mov(Operand::StaticVariable(context_address), esi); @@ -518,14 +518,14 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles) { void MacroAssembler::LeaveExitFrameEpilogue() { // Restore current context from top and clear it in debug mode. - ExternalReference context_address(Isolate::k_context_address, isolate()); + ExternalReference context_address(Isolate::kContextAddress, isolate()); mov(esi, Operand::StaticVariable(context_address)); #ifdef DEBUG mov(Operand::StaticVariable(context_address), Immediate(0)); #endif // Clear the top frame. - ExternalReference c_entry_fp_address(Isolate::k_c_entry_fp_address, + ExternalReference c_entry_fp_address(Isolate::kCEntryFPAddress, isolate()); mov(Operand::StaticVariable(c_entry_fp_address), Immediate(0)); } @@ -567,10 +567,10 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, push(Immediate(Smi::FromInt(0))); // No context. } // Save the current handler as the next handler. - push(Operand::StaticVariable(ExternalReference(Isolate::k_handler_address, + push(Operand::StaticVariable(ExternalReference(Isolate::kHandlerAddress, isolate()))); // Link this handler as the new current one. - mov(Operand::StaticVariable(ExternalReference(Isolate::k_handler_address, + mov(Operand::StaticVariable(ExternalReference(Isolate::kHandlerAddress, isolate())), esp); } @@ -578,7 +578,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, void MacroAssembler::PopTryHandler() { STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); - pop(Operand::StaticVariable(ExternalReference(Isolate::k_handler_address, + pop(Operand::StaticVariable(ExternalReference(Isolate::kHandlerAddress, isolate()))); add(Operand(esp), Immediate(StackHandlerConstants::kSize - kPointerSize)); } @@ -598,7 +598,7 @@ void MacroAssembler::Throw(Register value) { } // Drop the sp to the top of the handler. - ExternalReference handler_address(Isolate::k_handler_address, + ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); mov(esp, Operand::StaticVariable(handler_address)); @@ -637,7 +637,7 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, } // Drop sp to the top stack handler. - ExternalReference handler_address(Isolate::k_handler_address, + ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); mov(esp, Operand::StaticVariable(handler_address)); @@ -660,13 +660,13 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, if (type == OUT_OF_MEMORY) { // Set external caught exception to false. ExternalReference external_caught( - Isolate::k_external_caught_exception_address, + Isolate::kExternalCaughtExceptionAddress, isolate()); mov(eax, false); mov(Operand::StaticVariable(external_caught), eax); // Set pending exception and eax to out of memory exception. - ExternalReference pending_exception(Isolate::k_pending_exception_address, + ExternalReference pending_exception(Isolate::kPendingExceptionAddress, isolate()); mov(eax, reinterpret_cast<int32_t>(Failure::OutOfMemoryException())); mov(Operand::StaticVariable(pending_exception), eax); @@ -840,7 +840,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, NumberDictionary::kElementsStartOffset + 2 * kPointerSize; ASSERT_EQ(NORMAL, 0); test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset), - Immediate(PropertyDetails::TypeField::mask() << kSmiTagSize)); + Immediate(PropertyDetails::TypeField::kMask << kSmiTagSize)); j(not_zero, miss); // Get the value at the masked, scaled index. @@ -1172,7 +1172,7 @@ void MacroAssembler::AllocateAsciiString(Register result, } -void MacroAssembler::AllocateConsString(Register result, +void MacroAssembler::AllocateTwoByteConsString(Register result, Register scratch1, Register scratch2, Label* gc_required) { @@ -1208,7 +1208,7 @@ void MacroAssembler::AllocateAsciiConsString(Register result, } -void MacroAssembler::AllocateSlicedString(Register result, +void MacroAssembler::AllocateTwoByteSlicedString(Register result, Register scratch1, Register scratch2, Label* gc_required) { diff --git a/src/ia32/macro-assembler-ia32.h b/src/ia32/macro-assembler-ia32.h index de9361da..1906644c 100644 --- a/src/ia32/macro-assembler-ia32.h +++ b/src/ia32/macro-assembler-ia32.h @@ -437,7 +437,7 @@ class MacroAssembler: public Assembler { // Allocate a raw cons string object. Only the map field of the result is // initialized. - void AllocateConsString(Register result, + void AllocateTwoByteConsString(Register result, Register scratch1, Register scratch2, Label* gc_required); @@ -448,7 +448,7 @@ class MacroAssembler: public Assembler { // Allocate a raw sliced string object. Only the map field of the result is // initialized. - void AllocateSlicedString(Register result, + void AllocateTwoByteSlicedString(Register result, Register scratch1, Register scratch2, Label* gc_required); diff --git a/src/ia32/stub-cache-ia32.cc b/src/ia32/stub-cache-ia32.cc index 621a9bbf..ab62764e 100644 --- a/src/ia32/stub-cache-ia32.cc +++ b/src/ia32/stub-cache-ia32.cc @@ -2679,7 +2679,7 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { // -- esp[0] : return address // ----------------------------------- Code* stub; - JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + ElementsKind elements_kind = receiver_map->elements_kind(); bool is_jsarray = receiver_map->instance_type() == JS_ARRAY_TYPE; MaybeObject* maybe_stub = KeyedStoreElementStub(is_jsarray, elements_kind).TryGetCode(); @@ -3140,7 +3140,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { // -- esp[0] : return address // ----------------------------------- Code* stub; - JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + ElementsKind elements_kind = receiver_map->elements_kind(); MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(edx, @@ -3385,7 +3385,7 @@ void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( void KeyedLoadStubCompiler::GenerateLoadExternalArray( MacroAssembler* masm, - JSObject::ElementsKind elements_kind) { + ElementsKind elements_kind) { // ----------- S t a t e ------------- // -- eax : key // -- edx : receiver @@ -3407,29 +3407,29 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ mov(ebx, FieldOperand(ebx, ExternalArray::kExternalPointerOffset)); // ebx: base pointer of external storage switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: __ SmiUntag(eax); // Untag the index. __ movsx_b(eax, Operand(ebx, eax, times_1, 0)); break; - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: __ SmiUntag(eax); // Untag the index. __ movzx_b(eax, Operand(ebx, eax, times_1, 0)); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: __ movsx_w(eax, Operand(ebx, eax, times_1, 0)); break; - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ movzx_w(eax, Operand(ebx, eax, times_1, 0)); break; - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: - case JSObject::EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: __ mov(ecx, Operand(ebx, eax, times_2, 0)); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: __ fld_s(Operand(ebx, eax, times_2, 0)); break; - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: __ fld_d(Operand(ebx, eax, times_4, 0)); break; default: @@ -3442,17 +3442,17 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( // For floating-point array type: // FP(0): value - if (elements_kind == JSObject::EXTERNAL_INT_ELEMENTS || - elements_kind == JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS) { + if (elements_kind == EXTERNAL_INT_ELEMENTS || + elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) { // For the Int and UnsignedInt array types, we need to see whether // the value can be represented in a Smi. If not, we need to convert // it to a HeapNumber. Label box_int; - if (elements_kind == JSObject::EXTERNAL_INT_ELEMENTS) { + if (elements_kind == EXTERNAL_INT_ELEMENTS) { __ cmp(ecx, 0xC0000000); __ j(sign, &box_int); } else { - ASSERT_EQ(JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS, elements_kind); + ASSERT_EQ(EXTERNAL_UNSIGNED_INT_ELEMENTS, elements_kind); // The test is different for unsigned int values. Since we need // the value to be in the range of a positive smi, we can't // handle either of the top two bits being set in the value. @@ -3468,12 +3468,12 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( // Allocate a HeapNumber for the int and perform int-to-double // conversion. - if (elements_kind == JSObject::EXTERNAL_INT_ELEMENTS) { + if (elements_kind == EXTERNAL_INT_ELEMENTS) { __ push(ecx); __ fild_s(Operand(esp, 0)); __ pop(ecx); } else { - ASSERT_EQ(JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS, elements_kind); + ASSERT_EQ(EXTERNAL_UNSIGNED_INT_ELEMENTS, elements_kind); // Need to zero-extend the value. // There's no fild variant for unsigned values, so zero-extend // to a 64-bit int manually. @@ -3489,8 +3489,8 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ mov(eax, ecx); __ fstp_d(FieldOperand(eax, HeapNumber::kValueOffset)); __ ret(0); - } else if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS || - elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_FLOAT_ELEMENTS || + elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { // For the floating-point array type, we need to always allocate a // HeapNumber. __ AllocateHeapNumber(ecx, ebx, edi, &failed_allocation); @@ -3540,7 +3540,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( void KeyedStoreStubCompiler::GenerateStoreExternalArray( MacroAssembler* masm, - JSObject::ElementsKind elements_kind) { + ElementsKind elements_kind) { // ----------- S t a t e ------------- // -- eax : key // -- edx : receiver @@ -3566,7 +3566,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // edx: receiver // ecx: key // edi: elements array - if (elements_kind == JSObject::EXTERNAL_PIXEL_ELEMENTS) { + if (elements_kind == EXTERNAL_PIXEL_ELEMENTS) { __ JumpIfNotSmi(eax, &slow); } else { __ JumpIfNotSmi(eax, &check_heap_number); @@ -3578,33 +3578,33 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset)); // edi: base pointer of external storage switch (elements_kind) { - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: __ ClampUint8(ebx); __ SmiUntag(ecx); __ mov_b(Operand(edi, ecx, times_1, 0), ebx); break; - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ SmiUntag(ecx); __ mov_b(Operand(edi, ecx, times_1, 0), ebx); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ mov_w(Operand(edi, ecx, times_1, 0), ebx); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ mov(Operand(edi, ecx, times_2, 0), ebx); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: // Need to perform int-to-float conversion. __ push(ebx); __ fild_s(Operand(esp, 0)); __ pop(ebx); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { __ fstp_s(Operand(edi, ecx, times_2, 0)); - } else { // elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS. + } else { // elements_kind == EXTERNAL_DOUBLE_ELEMENTS. __ fstp_d(Operand(edi, ecx, times_4, 0)); } break; @@ -3615,7 +3615,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ ret(0); // Return the original value. // TODO(danno): handle heap number -> pixel array conversion - if (elements_kind != JSObject::EXTERNAL_PIXEL_ELEMENTS) { + if (elements_kind != EXTERNAL_PIXEL_ELEMENTS) { __ bind(&check_heap_number); // eax: value // edx: receiver @@ -3630,11 +3630,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // reproducible behavior, convert these to zero. __ mov(edi, FieldOperand(edi, ExternalArray::kExternalPointerOffset)); // edi: base pointer of external storage - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset)); __ fstp_s(Operand(edi, ecx, times_2, 0)); __ ret(0); - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { __ fld_d(FieldOperand(eax, HeapNumber::kValueOffset)); __ fstp_d(Operand(edi, ecx, times_4, 0)); __ ret(0); @@ -3647,23 +3647,23 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // (code-stubs-ia32.cc) is roughly what is needed here though the // conversion failure case does not need to be handled. if (CpuFeatures::IsSupported(SSE2)) { - if (elements_kind != JSObject::EXTERNAL_INT_ELEMENTS && - elements_kind != JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS) { + if (elements_kind != EXTERNAL_INT_ELEMENTS && + elements_kind != EXTERNAL_UNSIGNED_INT_ELEMENTS) { ASSERT(CpuFeatures::IsSupported(SSE2)); CpuFeatures::Scope scope(SSE2); __ cvttsd2si(ebx, FieldOperand(eax, HeapNumber::kValueOffset)); // ecx: untagged integer value switch (elements_kind) { - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: __ ClampUint8(ebx); // Fall through. - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ SmiUntag(ecx); __ mov_b(Operand(edi, ecx, times_1, 0), ebx); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ mov_w(Operand(edi, ecx, times_1, 0), ebx); break; default: @@ -61,8 +61,7 @@ static char TransitionMarkFromState(IC::State state) { 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 @@ void IC::TraceIC(const char* type, } 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 @@ void CallICBase::Clear(Address address, Code* target) { 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 @@ MaybeObject* CallICBase::ComputeMonomorphicStub( 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 @@ MaybeObject* CallICBase::ComputeMonomorphicStub( JSFunction* function = lookup->GetConstantFunction(); maybe_code = isolate()->stub_cache()->ComputeCallConstant(argc, - in_loop, kind_, extra_ic_state, *name, @@ -646,7 +640,6 @@ MaybeObject* CallICBase::ComputeMonomorphicStub( 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 @@ MaybeObject* CallICBase::ComputeMonomorphicStub( // 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 @@ void CallICBase::UpdateCaches(LookupResult* lookup, // 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 @@ void CallICBase::UpdateCaches(LookupResult* lookup, // 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 @@ void CallICBase::UpdateCaches(LookupResult* lookup, } else { maybe_code = isolate()->stub_cache()->ComputeCallMegamorphic(argc, - in_loop, kind_, extra_ic_state); } @@ -776,7 +765,7 @@ void CallICBase::UpdateCaches(LookupResult* lookup, #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 @@ MaybeObject* KeyedCallIC::LoadFunction(State state, 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 } } @@ -1093,7 +1079,7 @@ void LoadIC::UpdateCaches(LookupResult* lookup, MaybeObject* KeyedLoadIC::GetElementStubWithoutMapCheck( bool is_js_array, - JSObject::ElementsKind elements_kind) { + ElementsKind elements_kind) { return KeyedLoadElementStub(elements_kind).TryGetCode(); } @@ -1650,7 +1636,6 @@ MaybeObject* KeyedIC::ComputeStub(JSObject* receiver, 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); @@ -1721,7 +1706,7 @@ MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver, MaybeObject* KeyedStoreIC::GetElementStubWithoutMapCheck( bool is_js_array, - JSObject::ElementsKind elements_kind) { + ElementsKind elements_kind) { return KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); } @@ -1905,16 +1890,11 @@ void KeyedStoreIC::UpdateCaches(LookupResult* lookup, // 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 @@ RUNTIME_FUNCTION(MaybeObject*, CallIC_Miss) { 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 @@ RUNTIME_FUNCTION(MaybeObject*, KeyedCallIC_Miss) { 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)); } @@ -149,8 +149,7 @@ class IC { 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, @@ -348,7 +347,7 @@ class KeyedIC: public IC { virtual MaybeObject* GetElementStubWithoutMapCheck( bool is_js_array, - JSObject::ElementsKind elements_kind) = 0; + ElementsKind elements_kind) = 0; protected: virtual Code* string_stub() { @@ -415,7 +414,7 @@ class KeyedLoadIC: public KeyedIC { virtual MaybeObject* GetElementStubWithoutMapCheck( bool is_js_array, - JSObject::ElementsKind elements_kind); + ElementsKind elements_kind); protected: virtual Code::Kind kind() const { return Code::KEYED_LOAD_IC; } @@ -566,7 +565,7 @@ class KeyedStoreIC: public KeyedIC { virtual MaybeObject* GetElementStubWithoutMapCheck( bool is_js_array, - JSObject::ElementsKind elements_kind); + ElementsKind elements_kind); protected: virtual Code::Kind kind() const { return Code::KEYED_STORE_IC; } diff --git a/src/inspector.h b/src/inspector.h index f8b30428..e328bcdf 100644 --- a/src/inspector.h +++ b/src/inspector.h @@ -41,7 +41,6 @@ namespace internal { class Inspector { public: - static void DumpObjectType(FILE* out, Object *obj, bool print_more); static void DumpObjectType(FILE* out, Object *obj) { DumpObjectType(out, obj, false); @@ -59,4 +58,3 @@ class Inspector { #endif // INSPECTOR #endif // V8_INSPECTOR_H_ - diff --git a/src/isolate.cc b/src/isolate.cc index 09cbc8a1..fd0f673e 100644 --- a/src/isolate.cc +++ b/src/isolate.cc @@ -43,7 +43,6 @@ #include "messages.h" #include "regexp-stack.h" #include "runtime-profiler.h" -#include "scanner.h" #include "scopeinfo.h" #include "serialize.h" #include "simulator.h" @@ -1409,14 +1408,13 @@ Isolate::Isolate() global_handles_(NULL), context_switcher_(NULL), thread_manager_(NULL), - ast_sentinels_(NULL), string_tracker_(NULL), regexp_stack_(NULL), embedder_data_(NULL) { TRACE_ISOLATE(constructor); memset(isolate_addresses_, 0, - sizeof(isolate_addresses_[0]) * (k_isolate_address_count + 1)); + sizeof(isolate_addresses_[0]) * (kIsolateAddressCount + 1)); heap_.isolate_ = this; zone_.isolate_ = this; @@ -1546,9 +1544,6 @@ Isolate::~Isolate() { delete regexp_stack_; regexp_stack_ = NULL; - delete ast_sentinels_; - ast_sentinels_ = NULL; - delete descriptor_lookup_cache_; descriptor_lookup_cache_ = NULL; delete context_slot_cache_; @@ -1691,9 +1686,10 @@ bool Isolate::Init(Deserializer* des) { // ensuring that Isolate::Current() == this. heap_.SetStackLimits(); -#define C(name) isolate_addresses_[Isolate::k_##name] = \ - reinterpret_cast<Address>(name()); - ISOLATE_ADDRESS_LIST(C) +#define ASSIGN_ELEMENT(CamelName, hacker_name) \ + isolate_addresses_[Isolate::k##CamelName##Address] = \ + reinterpret_cast<Address>(hacker_name##_address()); + FOR_EACH_ISOLATE_ADDRESS_NAME(ASSIGN_ELEMENT) #undef C string_tracker_ = new StringTracker(); @@ -1710,7 +1706,6 @@ bool Isolate::Init(Deserializer* des) { bootstrapper_ = new Bootstrapper(); handle_scope_implementer_ = new HandleScopeImplementer(this); stub_cache_ = new StubCache(this); - ast_sentinels_ = new AstSentinels(); regexp_stack_ = new RegExpStack(); regexp_stack_->isolate_ = this; diff --git a/src/isolate.h b/src/isolate.h index 5bb504d2..2582da64 100644 --- a/src/isolate.h +++ b/src/isolate.h @@ -47,7 +47,6 @@ namespace v8 { namespace internal { -class AstSentinels; class Bootstrapper; class CodeGenerator; class CodeRange; @@ -120,13 +119,13 @@ typedef ZoneList<Handle<Object> > ZoneObjectList; #define RETURN_IF_EMPTY_HANDLE(isolate, call) \ RETURN_IF_EMPTY_HANDLE_VALUE(isolate, call, Failure::Exception()) -#define ISOLATE_ADDRESS_LIST(C) \ - C(handler_address) \ - C(c_entry_fp_address) \ - C(context_address) \ - C(pending_exception_address) \ - C(external_caught_exception_address) \ - C(js_entry_sp_address) +#define FOR_EACH_ISOLATE_ADDRESS_NAME(C) \ + C(Handler, handler) \ + C(CEntryFP, c_entry_fp) \ + C(Context, context) \ + C(PendingException, pending_exception) \ + C(ExternalCaughtException, external_caught_exception) \ + C(JSEntrySP, js_entry_sp) // Platform-independent, reliable thread identifier. @@ -424,10 +423,10 @@ class Isolate { enum AddressId { -#define C(name) k_##name, - ISOLATE_ADDRESS_LIST(C) +#define DECLARE_ENUM(CamelName, hacker_name) k##CamelName##Address, + FOR_EACH_ISOLATE_ADDRESS_NAME(DECLARE_ENUM) #undef C - k_isolate_address_count + kIsolateAddressCount }; // Returns the PerIsolateThreadData for the current thread (or NULL if one is @@ -878,8 +877,6 @@ class Isolate { return &objects_string_input_buffer_; } - AstSentinels* ast_sentinels() { return ast_sentinels_; } - RuntimeState* runtime_state() { return &runtime_state_; } StaticResource<SafeStringInputBuffer>* compiler_safe_string_input_buffer() { @@ -1100,7 +1097,7 @@ class Isolate { StringStream* incomplete_message_; // The preallocated memory thread singleton. PreallocatedMemoryThread* preallocated_memory_thread_; - Address isolate_addresses_[k_isolate_address_count + 1]; // NOLINT + Address isolate_addresses_[kIsolateAddressCount + 1]; // NOLINT NoAllocationStringAllocator* preallocated_message_space_; Bootstrapper* bootstrapper_; @@ -1138,7 +1135,6 @@ class Isolate { GlobalHandles* global_handles_; ContextSwitcher* context_switcher_; ThreadManager* thread_manager_; - AstSentinels* ast_sentinels_; RuntimeState runtime_state_; StaticResource<SafeStringInputBuffer> compiler_safe_string_input_buffer_; Builtins builtins_; diff --git a/src/json.js b/src/json.js index 8fd410fa..deba1262 100644 --- a/src/json.js +++ b/src/json.js @@ -54,7 +54,7 @@ function Revive(holder, name, reviver) { 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 @@ function JSONSerialize(key, holder, replacer, stack, indent, gap) { 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 BasicSerializeObject(value, stack, builder) { 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); } } @@ -337,11 +337,12 @@ function JSONStringify(value, replacer, space) { return JSONSerialize('', {'': value}, replacer, new InternalArray(), "", gap); } -function SetupJSON() { +function SetUpJSON() { + %CheckIsBootstrapping(); InstallFunctions($JSON, DONT_ENUM, $Array( "parse", JSONParse, "stringify", JSONStringify )); } -SetupJSON(); +SetUpJSON() diff --git a/src/jsregexp.cc b/src/jsregexp.cc index 4ca83a47..3ebfbdfc 100644 --- a/src/jsregexp.cc +++ b/src/jsregexp.cc @@ -2661,7 +2661,8 @@ int TextNode::GreedyLoopTextLength() { // this alternative and back to this choice node. If there are variable // length nodes or other complications in the way then return a sentinel // value indicating that a greedy loop cannot be constructed. -int ChoiceNode::GreedyLoopTextLength(GuardedAlternative* alternative) { +int ChoiceNode::GreedyLoopTextLengthForAlternative( + GuardedAlternative* alternative) { int length = 0; RegExpNode* node = alternative->node(); // Later we will generate code for all these text nodes using recursion @@ -2700,7 +2701,8 @@ void LoopChoiceNode::AddContinueAlternative(GuardedAlternative alt) { void LoopChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) { RegExpMacroAssembler* macro_assembler = compiler->macro_assembler(); if (trace->stop_node() == this) { - int text_length = GreedyLoopTextLength(&(alternatives_->at(0))); + int text_length = + GreedyLoopTextLengthForAlternative(&(alternatives_->at(0))); ASSERT(text_length != kNodeIsTooComplexForGreedyLoops); // Update the counter-based backtracking info on the stack. This is an // optimization for greedy loops (see below). @@ -2893,7 +2895,7 @@ void ChoiceNode::Emit(RegExpCompiler* compiler, Trace* trace) { Trace* current_trace = trace; - int text_length = GreedyLoopTextLength(&(alternatives_->at(0))); + int text_length = GreedyLoopTextLengthForAlternative(&(alternatives_->at(0))); bool greedy_loop = false; Label greedy_loop_label; Trace counter_backtrack_trace; diff --git a/src/jsregexp.h b/src/jsregexp.h index 13f9e2ea..54297a49 100644 --- a/src/jsregexp.h +++ b/src/jsregexp.h @@ -255,6 +255,7 @@ class SetRelation BASE_EMBEDDED { return (bits_ == (kInFirst | kInSecond | kInBoth)); } int value() { return bits_; } + private: int bits_; }; @@ -404,6 +405,7 @@ class DispatchTable : public ZoneObject { template <typename Callback> void ForEach(Callback* callback) { return tree()->ForEach(callback); } + private: // There can't be a static empty set since it allocates its // successors in a zone and caches them. @@ -793,6 +795,7 @@ class ActionNode: public SeqRegExpNode { virtual int GreedyLoopTextLength() { return kNodeIsTooComplexForGreedyLoops; } virtual ActionNode* Clone() { return new ActionNode(*this); } virtual int ComputeFirstCharacterSet(int budget); + private: union { struct { @@ -861,6 +864,7 @@ class TextNode: public SeqRegExpNode { } void CalculateOffsets(); virtual int ComputeFirstCharacterSet(int budget); + private: enum TextEmitPassType { NON_ASCII_MATCH, // Check for characters that can't match. @@ -925,6 +929,7 @@ class AssertionNode: public SeqRegExpNode { virtual AssertionNode* Clone() { return new AssertionNode(*this); } AssertionNodeType type() { return type_; } void set_type(AssertionNodeType type) { type_ = type; } + private: AssertionNode(AssertionNodeType t, RegExpNode* on_success) : SeqRegExpNode(on_success), type_(t) { } @@ -955,6 +960,7 @@ class BackReferenceNode: public SeqRegExpNode { } virtual BackReferenceNode* Clone() { return new BackReferenceNode(*this); } virtual int ComputeFirstCharacterSet(int budget); + private: int start_reg_; int end_reg_; @@ -1071,7 +1077,7 @@ class ChoiceNode: public RegExpNode { virtual bool try_to_emit_quick_check_for_alternative(int i) { return true; } protected: - int GreedyLoopTextLength(GuardedAlternative* alternative); + int GreedyLoopTextLengthForAlternative(GuardedAlternative* alternative); ZoneList<GuardedAlternative>* alternatives_; private: @@ -1301,6 +1307,7 @@ class Trace { } void InvalidateCurrentCharacter(); void AdvanceCurrentPositionInTrace(int by, RegExpCompiler* compiler); + private: int FindAffectedRegisters(OutSet* affected_registers); void PerformDeferredActions(RegExpMacroAssembler* macro, @@ -1402,6 +1409,7 @@ FOR_EACH_NODE_TYPE(DECLARE_VISIT) void fail(const char* error_message) { error_message_ = error_message; } + private: bool ignore_case_; bool is_ascii_; diff --git a/src/list-inl.h b/src/list-inl.h index 8ef7514f..80bccc9b 100644 --- a/src/list-inl.h +++ b/src/list-inl.h @@ -207,6 +207,35 @@ void List<T, P>::Initialize(int capacity) { } +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_ @@ -49,7 +49,6 @@ namespace internal { template <typename T, class P> class List { public: - List() { Initialize(0); } INLINE(explicit List(int capacity)) { Initialize(capacity); } INLINE(~List()) { DeleteData(data_); } @@ -169,6 +168,15 @@ class Code; 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/lithium.cc b/src/lithium.cc index 64ef469b..5410f6f0 100644 --- a/src/lithium.cc +++ b/src/lithium.cc @@ -166,25 +166,25 @@ void LPointerMap::PrintTo(StringStream* stream) { } -int ElementsKindToShiftSize(JSObject::ElementsKind elements_kind) { +int ElementsKindToShiftSize(ElementsKind elements_kind) { switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: return 0; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: return 1; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: - case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: return 2; - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: return 3; - case JSObject::FAST_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case FAST_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: return kPointerSizeLog2; } UNREACHABLE(); diff --git a/src/lithium.h b/src/lithium.h index 6010b777..20da21a6 100644 --- a/src/lithium.h +++ b/src/lithium.h @@ -165,8 +165,7 @@ class LUnallocated: public LOperand { } Policy policy() const { return PolicyField::decode(value_); } void set_policy(Policy policy) { - value_ &= ~PolicyField::mask(); - value_ |= PolicyField::encode(policy); + value_ = PolicyField::update(value_, policy); } int fixed_index() const { return static_cast<int>(value_) >> kFixedIndexShift; @@ -177,8 +176,7 @@ class LUnallocated: public LOperand { } void set_virtual_register(unsigned id) { - value_ &= ~VirtualRegisterField::mask(); - value_ |= VirtualRegisterField::encode(id); + value_ = VirtualRegisterField::update(value_, id); } LUnallocated* CopyUnconstrained() { @@ -586,7 +584,7 @@ class DeepIterator BASE_EMBEDDED { }; -int ElementsKindToShiftSize(JSObject::ElementsKind elements_kind); +int ElementsKindToShiftSize(ElementsKind elements_kind); } } // namespace v8::internal diff --git a/src/liveedit.cc b/src/liveedit.cc index 0b01e8af..d44c2fc1 100644 --- a/src/liveedit.cc +++ b/src/liveedit.cc @@ -860,8 +860,7 @@ class FunctionInfoListener { int j = 0; for (int i = 0; i < list.length(); i++) { Variable* var1 = list[i]; - Slot* slot = var1->AsSlot(); - if (slot != NULL && slot->type() == Slot::CONTEXT) { + if (var1->IsContextSlot()) { if (j != i) { list[j] = var1; } @@ -873,7 +872,7 @@ class FunctionInfoListener { for (int k = 1; k < j; k++) { int l = k; for (int m = k + 1; m < j; m++) { - if (list[l]->AsSlot()->index() > list[m]->AsSlot()->index()) { + if (list[l]->index() > list[m]->index()) { l = m; } } @@ -887,7 +886,7 @@ class FunctionInfoListener { SetElementNonStrict( scope_info_list, scope_info_length, - Handle<Smi>(Smi::FromInt(list[i]->AsSlot()->index()))); + Handle<Smi>(Smi::FromInt(list[i]->index()))); scope_info_length++; } SetElementNonStrict(scope_info_list, @@ -1451,7 +1450,7 @@ static bool FixTryCatchHandler(StackFrame* top_frame, StackFrame* bottom_frame) { Address* pointer_address = &Memory::Address_at(Isolate::Current()->get_address_from_id( - Isolate::k_handler_address)); + Isolate::kHandlerAddress)); while (*pointer_address < top_frame->sp()) { pointer_address = &Memory::Address_at(*pointer_address); diff --git a/src/liveobjectlist.cc b/src/liveobjectlist.cc index 451a28ab..957c0515 100644 --- a/src/liveobjectlist.cc +++ b/src/liveobjectlist.cc @@ -184,7 +184,7 @@ bool IsOfType(LiveObjectType type, HeapObject *obj) { const AllocationSpace kInvalidSpace = static_cast<AllocationSpace>(-1); static AllocationSpace FindSpaceFor(String* space_str) { - SmartPointer<char> s = + SmartArrayPointer<char> s = space_str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); const char* key_str = *s; @@ -236,7 +236,7 @@ static bool InSpace(AllocationSpace space, HeapObject *heap_obj) { static LiveObjectType FindTypeFor(String* type_str) { - SmartPointer<char> s = + SmartArrayPointer<char> s = type_str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); #define CHECK_OBJECT_TYPE(type_, name) { \ @@ -503,10 +503,10 @@ static void GenerateObjectDesc(HeapObject* obj, // We'll only dump 80 of them after we compact them. const int kMaxCharToDump = 80; const int kMaxBufferSize = kMaxCharToDump * 2; - SmartPointer<char> str_sp = str->ToCString(DISALLOW_NULLS, - ROBUST_STRING_TRAVERSAL, - 0, - kMaxBufferSize); + SmartArrayPointer<char> str_sp = str->ToCString(DISALLOW_NULLS, + ROBUST_STRING_TRAVERSAL, + 0, + kMaxBufferSize); char* str_cstr = *str_sp; int length = CompactString(str_cstr); OS::SNPrintF(buffer_v, @@ -526,14 +526,14 @@ static void GenerateObjectDesc(HeapObject* obj, } String* name = sinfo->DebugName(); - SmartPointer<char> name_sp = + SmartArrayPointer<char> name_sp = name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); char* name_cstr = *name_sp; HeapStringAllocator string_allocator; StringStream stream(&string_allocator); sinfo->SourceCodePrint(&stream, 50); - SmartPointer<const char> source_sp = stream.ToCString(); + SmartArrayPointer<const char> source_sp = stream.ToCString(); const char* source_cstr = *source_sp; OS::SNPrintF(buffer_v, @@ -1656,7 +1656,7 @@ int LiveObjectList::GetObjId(Object* obj) { // Gets the obj id for the specified address if valid. Object* LiveObjectList::GetObjId(Handle<String> address) { - SmartPointer<char> addr_str = + SmartArrayPointer<char> addr_str = address->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); Isolate* isolate = Isolate::Current(); diff --git a/src/liveobjectlist.h b/src/liveobjectlist.h index 542482d9..65470d7a 100644 --- a/src/liveobjectlist.h +++ b/src/liveobjectlist.h @@ -114,7 +114,6 @@ class LiveObjectList { static Object* PrintObj(int obj_id); private: - struct Element { int id_; HeapObject* obj_; @@ -224,7 +223,6 @@ class LiveObjectList { // Helper class for updating the LiveObjectList HeapObject pointers. class UpdateLiveObjectListVisitor: public ObjectVisitor { public: - void VisitPointer(Object** p) { UpdatePointer(p); } void VisitPointers(Object** start, Object** end) { @@ -319,4 +317,3 @@ class LiveObjectList { } } // namespace v8::internal #endif // V8_LIVEOBJECTLIST_H_ - diff --git a/src/log-utils.cc b/src/log-utils.cc index 27e654d5..7bd7baa2 100644 --- a/src/log-utils.cc +++ b/src/log-utils.cc @@ -125,7 +125,7 @@ void Log::Initialize() { stream.Put(*p); } } - SmartPointer<const char> expanded = stream.ToCString(); + SmartArrayPointer<const char> expanded = stream.ToCString(); OpenFile(*expanded); } else { OpenFile(FLAG_logfile); diff --git a/src/log-utils.h b/src/log-utils.h index 2b20a01b..d0cb8289 100644 --- a/src/log-utils.h +++ b/src/log-utils.h @@ -141,7 +141,6 @@ class LogMessageBuilder BASE_EMBEDDED { void WriteToLogFile(); private: - Log* log_; ScopedLock sl; int pos_; @@ -617,7 +617,7 @@ void Logger::ApiEvent(const char* format, ...) { void Logger::ApiNamedSecurityCheck(Object* key) { if (!log_->IsEnabled() || !FLAG_log_api) return; if (key->IsString()) { - SmartPointer<char> str = + SmartArrayPointer<char> str = String::cast(key)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); ApiEvent("api,check-security,\"%s\"\n", *str); } else if (key->IsUndefined()) { @@ -762,9 +762,9 @@ void Logger::ApiNamedPropertyAccess(const char* tag, ASSERT(name->IsString()); if (!log_->IsEnabled() || !FLAG_log_api) return; String* class_name_obj = holder->class_name(); - SmartPointer<char> class_name = + SmartArrayPointer<char> class_name = class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); - SmartPointer<char> property_name = + SmartArrayPointer<char> property_name = String::cast(name)->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); ApiEvent("api,%s,\"%s\",\"%s\"\n", tag, *class_name, *property_name); } @@ -774,7 +774,7 @@ void Logger::ApiIndexedPropertyAccess(const char* tag, uint32_t index) { if (!log_->IsEnabled() || !FLAG_log_api) return; String* class_name_obj = holder->class_name(); - SmartPointer<char> class_name = + SmartArrayPointer<char> class_name = class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); ApiEvent("api,%s,\"%s\",%u\n", tag, *class_name, index); } @@ -782,7 +782,7 @@ void Logger::ApiIndexedPropertyAccess(const char* tag, void Logger::ApiObjectAccess(const char* tag, JSObject* object) { if (!log_->IsEnabled() || !FLAG_log_api) return; String* class_name_obj = object->class_name(); - SmartPointer<char> class_name = + SmartArrayPointer<char> class_name = class_name_obj->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); ApiEvent("api,%s,\"%s\"\n", tag, *class_name); } @@ -836,7 +836,7 @@ void Logger::CallbackEventInternal(const char* prefix, const char* name, void Logger::CallbackEvent(String* name, Address entry_point) { if (!log_->IsEnabled() || !FLAG_log_code) return; - SmartPointer<char> str = + SmartArrayPointer<char> str = name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); CallbackEventInternal("", *str, entry_point); } @@ -844,7 +844,7 @@ void Logger::CallbackEvent(String* name, Address entry_point) { void Logger::GetterCallbackEvent(String* name, Address entry_point) { if (!log_->IsEnabled() || !FLAG_log_code) return; - SmartPointer<char> str = + SmartArrayPointer<char> str = name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); CallbackEventInternal("get ", *str, entry_point); } @@ -852,7 +852,7 @@ void Logger::GetterCallbackEvent(String* name, Address entry_point) { void Logger::SetterCallbackEvent(String* name, Address entry_point) { if (!log_->IsEnabled() || !FLAG_log_code) return; - SmartPointer<char> str = + SmartArrayPointer<char> str = name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); CallbackEventInternal("set ", *str, entry_point); } @@ -957,7 +957,7 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, return; LogMessageBuilder msg(this); - SmartPointer<char> str = + SmartArrayPointer<char> str = name->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); msg.Append("%s,%s,", kLogEventsNames[CODE_CREATION_EVENT], @@ -998,9 +998,9 @@ void Logger::CodeCreateEvent(LogEventsAndTags tag, } if (!FLAG_log_code) return; LogMessageBuilder msg(this); - SmartPointer<char> name = + SmartArrayPointer<char> name = shared->DebugName()->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); - SmartPointer<char> sourcestr = + SmartArrayPointer<char> sourcestr = source->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); msg.Append("%s,%s,", kLogEventsNames[CODE_CREATION_EVENT], @@ -1527,6 +1527,51 @@ void Logger::LogCodeObjects() { } +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 @@ void Logger::LogCompiledFunctions() { 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]); } } @@ -281,6 +281,8 @@ class Logger { 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 5ba7ac3a..7a493ca7 100644 --- a/src/macros.py +++ b/src/macros.py @@ -116,13 +116,21 @@ macro FLOOR(arg) = $floor(arg); # Macro for ECMAScript 5 queries of the type: # "Type(O) is object." -# This is the same as being either a function or an object in V8 terminology. +# 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)); -macro NUMBER_IS_FINITE(arg) = (%_IsSmi(%IS_VAR(arg)) || arg - arg == 0); +macro NUMBER_IS_FINITE(arg) = (%_IsSmi(%IS_VAR(arg)) || ((arg == arg) && (arg != 1/0) && (arg != -1/0))); macro TO_INTEGER(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToInteger(ToNumber(arg))); macro TO_INTEGER_MAP_MINUS_ZERO(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : %NumberToIntegerMapMinusZero(ToNumber(arg))); macro TO_INT32(arg) = (%_IsSmi(%IS_VAR(arg)) ? arg : (arg >> 0)); @@ -170,7 +178,7 @@ macro CAPTURE(index) = (3 + (index)); const CAPTURE0 = 3; const CAPTURE1 = 4; -# PropertyDescriptor return value indices - must match +# PropertyDescriptor return value indices - must match # PropertyDescriptorIndices in runtime.cc. const IS_ACCESSOR_INDEX = 0; const VALUE_INDEX = 1; @@ -179,3 +187,17 @@ const SETTER_INDEX = 3; const WRITABLE_INDEX = 4; const ENUMERABLE_INDEX = 5; const CONFIGURABLE_INDEX = 6; + +# For messages.js +# Matches Script::Type from objects.h +const TYPE_NATIVE = 0; +const TYPE_EXTENSION = 1; +const TYPE_NORMAL = 2; + +# Matches Script::CompilationType from objects.h +const COMPILATION_TYPE_HOST = 0; +const COMPILATION_TYPE_EVAL = 1; +const COMPILATION_TYPE_JSON = 2; + +# Matches Messages::kNoLineNumberInfo from v8.h +const kNoLineNumberInfo = 0; diff --git a/src/math.js b/src/math.js index 70b8c57c..b5a6d181 100644 --- a/src/math.js +++ b/src/math.js @@ -38,7 +38,7 @@ const $abs = MathAbs; function MathConstructor() {} %FunctionSetInstanceClassName(MathConstructor, 'Math'); const $Math = new MathConstructor(); -$Math.__proto__ = global.Object.prototype; +$Math.__proto__ = $Object.prototype; %SetProperty(global, "Math", $Math, DONT_ENUM); // ECMA 262 - 15.8.2.1 @@ -195,8 +195,9 @@ function MathTan(x) { // ------------------------------------------------------------------- -function SetupMath() { - // Setup math constants. +function SetUpMath() { + %CheckIsBootstrapping(); + // Set up math constants. // ECMA-262, section 15.8.1.1. %OptimizeObjectForAddingMultipleProperties($Math, 8); %SetProperty($Math, @@ -236,7 +237,7 @@ function SetupMath() { DONT_ENUM | DONT_DELETE | READ_ONLY); %ToFastProperties($Math); - // Setup non-enumerable functions of the Math object and + // Set up non-enumerable functions of the Math object and // set their names. InstallFunctionsOnHiddenPrototype($Math, DONT_ENUM, $Array( "random", MathRandom, @@ -258,7 +259,6 @@ function SetupMath() { "max", MathMax, "min", MathMin )); -}; - +} -SetupMath(); +SetUpMath(); diff --git a/src/messages.cc b/src/messages.cc index 4cbf0af7..b6ad5ac3 100644 --- a/src/messages.cc +++ b/src/messages.cc @@ -41,13 +41,13 @@ namespace internal { // by default. void MessageHandler::DefaultMessageReport(const MessageLocation* loc, Handle<Object> message_obj) { - SmartPointer<char> str = GetLocalizedMessage(message_obj); + SmartArrayPointer<char> str = GetLocalizedMessage(message_obj); if (loc == NULL) { PrintF("%s\n", *str); } else { HandleScope scope; Handle<Object> data(loc->script()->name()); - SmartPointer<char> data_str; + SmartArrayPointer<char> data_str; if (data->IsString()) data_str = Handle<String>::cast(data)->ToCString(DISALLOW_NULLS); PrintF("%s:%i: %s\n", *data_str ? *data_str : "<unknown>", @@ -170,7 +170,8 @@ Handle<String> MessageHandler::GetMessage(Handle<Object> data) { } -SmartPointer<char> MessageHandler::GetLocalizedMessage(Handle<Object> data) { +SmartArrayPointer<char> MessageHandler::GetLocalizedMessage( + Handle<Object> data) { HandleScope scope; return GetMessage(data)->ToCString(DISALLOW_NULLS); } diff --git a/src/messages.h b/src/messages.h index fc2162de..358509ec 100644 --- a/src/messages.h +++ b/src/messages.h @@ -105,7 +105,7 @@ class MessageHandler { static void DefaultMessageReport(const MessageLocation* loc, Handle<Object> message_obj); static Handle<String> GetMessage(Handle<Object> data); - static SmartPointer<char> GetLocalizedMessage(Handle<Object> data); + static SmartArrayPointer<char> GetLocalizedMessage(Handle<Object> data); }; } } // namespace v8::internal diff --git a/src/messages.js b/src/messages.js index 845ca077..a9993af2 100644 --- a/src/messages.js +++ b/src/messages.js @@ -28,27 +28,14 @@ // ------------------------------------------------------------------- // -// Matches Script::Type from objects.h -var TYPE_NATIVE = 0; -var TYPE_EXTENSION = 1; -var TYPE_NORMAL = 2; - -// Matches Script::CompilationType from objects.h -var COMPILATION_TYPE_HOST = 0; -var COMPILATION_TYPE_EVAL = 1; -var COMPILATION_TYPE_JSON = 2; - -// Matches Messages::kNoLineNumberInfo from v8.h -var kNoLineNumberInfo = 0; - // If this object gets passed to an error constructor the error will // get an accessor for .message that constructs a descriptive error // message on access. -var kAddMessageAccessorsMarker = { }; +const kAddMessageAccessorsMarker = { }; -var kMessages = 0; - -var kReplacementMarkers = [ "%0", "%1", "%2", "%3" ]; +// This will be lazily initialized when first needed (and forcibly +// overwritten even though it's const). +const kMessages = 0; function FormatString(format, message) { var args = %MessageGetArguments(message); @@ -56,14 +43,16 @@ function FormatString(format, message) { var arg_num = 0; for (var i = 0; i < format.length; i++) { var str = format[i]; - for (arg_num = 0; arg_num < kReplacementMarkers.length; arg_num++) { - if (str == kReplacementMarkers[arg_num]) { + if (str.length == 2 && %_StringCharCodeAt(str, 0) == 0x25) { + // Two-char string starts with "%". + var arg_num = (%_StringCharCodeAt(str, 1) - 0x30) >>> 0; + if (arg_num < 4) { + // str is one of %0, %1, %2 or %3. try { str = ToDetailString(args[arg_num]); } catch (e) { str = "#<error>"; } - break; } } result += str; @@ -102,18 +91,16 @@ function ToStringCheckErrorObject(obj) { function ToDetailString(obj) { - if (obj != null && IS_OBJECT(obj) && - obj.toString === $Object.prototype.toString) { + if (obj != null && IS_OBJECT(obj) && obj.toString === ObjectToString) { var constructor = obj.constructor; - if (!constructor) return ToStringCheckErrorObject(obj); - var constructorName = constructor.name; - if (!constructorName || !IS_STRING(constructorName)) { - return ToStringCheckErrorObject(obj); + if (typeof constructor == "function") { + var constructorName = constructor.name; + if (IS_STRING(constructorName) && constructorName !== "") { + return "#<" + constructorName + ">"; + } } - return "#<" + constructorName + ">"; - } else { - return ToStringCheckErrorObject(obj); } + return ToStringCheckErrorObject(obj); } @@ -129,10 +116,11 @@ function MakeGenericError(constructor, type, args) { /** - * Setup the Script function and constructor. + * Set up the Script function and constructor. */ %FunctionSetInstanceClassName(Script, 'Script'); -%SetProperty(Script.prototype, 'constructor', Script, DONT_ENUM); +%SetProperty(Script.prototype, 'constructor', Script, + DONT_ENUM | DONT_DELETE | READ_ONLY); %SetCode(Script, function(x) { // Script objects can only be created by the VM. throw new $Error("Not supported"); @@ -142,118 +130,135 @@ function MakeGenericError(constructor, type, args) { // Helper functions; called from the runtime system. function FormatMessage(message) { if (kMessages === 0) { - kMessages = { + var messagesDictionary = [ // Error - cyclic_proto: ["Cyclic __proto__ value"], - code_gen_from_strings: ["Code generation from strings disallowed for this context"], + "cyclic_proto", ["Cyclic __proto__ value"], + "code_gen_from_strings", ["Code generation from strings disallowed for this context"], // TypeError - unexpected_token: ["Unexpected token ", "%0"], - unexpected_token_number: ["Unexpected number"], - unexpected_token_string: ["Unexpected string"], - unexpected_token_identifier: ["Unexpected identifier"], - unexpected_reserved: ["Unexpected reserved word"], - unexpected_strict_reserved: ["Unexpected strict mode reserved word"], - unexpected_eos: ["Unexpected end of input"], - malformed_regexp: ["Invalid regular expression: /", "%0", "/: ", "%1"], - unterminated_regexp: ["Invalid regular expression: missing /"], - regexp_flags: ["Cannot supply flags when constructing one RegExp from another"], - incompatible_method_receiver: ["Method ", "%0", " called on incompatible receiver ", "%1"], - invalid_lhs_in_assignment: ["Invalid left-hand side in assignment"], - invalid_lhs_in_for_in: ["Invalid left-hand side in for-in"], - invalid_lhs_in_postfix_op: ["Invalid left-hand side expression in postfix operation"], - invalid_lhs_in_prefix_op: ["Invalid left-hand side expression in prefix operation"], - multiple_defaults_in_switch: ["More than one default clause in switch statement"], - newline_after_throw: ["Illegal newline after throw"], - redeclaration: ["%0", " '", "%1", "' has already been declared"], - no_catch_or_finally: ["Missing catch or finally after try"], - unknown_label: ["Undefined label '", "%0", "'"], - uncaught_exception: ["Uncaught ", "%0"], - stack_trace: ["Stack Trace:\n", "%0"], - called_non_callable: ["%0", " is not a function"], - undefined_method: ["Object ", "%1", " has no method '", "%0", "'"], - property_not_function: ["Property '", "%0", "' of object ", "%1", " is not a function"], - cannot_convert_to_primitive: ["Cannot convert object to primitive value"], - not_constructor: ["%0", " is not a constructor"], - not_defined: ["%0", " is not defined"], - non_object_property_load: ["Cannot read property '", "%0", "' of ", "%1"], - non_object_property_store: ["Cannot set property '", "%0", "' of ", "%1"], - non_object_property_call: ["Cannot call method '", "%0", "' of ", "%1"], - with_expression: ["%0", " has no properties"], - illegal_invocation: ["Illegal invocation"], - no_setter_in_callback: ["Cannot set property ", "%0", " of ", "%1", " which has only a getter"], - apply_non_function: ["Function.prototype.apply was called on ", "%0", ", which is a ", "%1", " and not a function"], - apply_wrong_args: ["Function.prototype.apply: Arguments list has wrong type"], - invalid_in_operator_use: ["Cannot use 'in' operator to search for '", "%0", "' in ", "%1"], - instanceof_function_expected: ["Expecting a function in instanceof check, but got ", "%0"], - instanceof_nonobject_proto: ["Function has non-object prototype '", "%0", "' in instanceof check"], - null_to_object: ["Cannot convert null to object"], - reduce_no_initial: ["Reduce of empty array with no initial value"], - getter_must_be_callable: ["Getter must be a function: ", "%0"], - setter_must_be_callable: ["Setter must be a function: ", "%0"], - value_and_accessor: ["Invalid property. A property cannot both have accessors and be writable or have a value: ", "%0"], - proto_object_or_null: ["Object prototype may only be an Object or null"], - property_desc_object: ["Property description must be an object: ", "%0"], - redefine_disallowed: ["Cannot redefine property: ", "%0"], - 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"], - 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"], - handler_returned_undefined: ["Proxy handler ", "%0", " returned undefined for '", "%1", "' trap"], - proxy_prop_not_configurable: ["Trap ", "%1", " of proxy handler ", "%0", " returned non-configurable descriptor for property ", "%2"], - proxy_non_object_prop_names: ["Trap ", "%1", " returned non-object ", "%0"], - proxy_repeated_prop_name: ["Trap ", "%1", " returned repeated property name ", "%2"], - invalid_weakmap_key: ["Invalid value used as weak map key"], + "unexpected_token", ["Unexpected token ", "%0"], + "unexpected_token_number", ["Unexpected number"], + "unexpected_token_string", ["Unexpected string"], + "unexpected_token_identifier", ["Unexpected identifier"], + "unexpected_reserved", ["Unexpected reserved word"], + "unexpected_strict_reserved", ["Unexpected strict mode reserved word"], + "unexpected_eos", ["Unexpected end of input"], + "malformed_regexp", ["Invalid regular expression: /", "%0", "/: ", "%1"], + "unterminated_regexp", ["Invalid regular expression: missing /"], + "regexp_flags", ["Cannot supply flags when constructing one RegExp from another"], + "incompatible_method_receiver", ["Method ", "%0", " called on incompatible receiver ", "%1"], + "invalid_lhs_in_assignment", ["Invalid left-hand side in assignment"], + "invalid_lhs_in_for_in", ["Invalid left-hand side in for-in"], + "invalid_lhs_in_postfix_op", ["Invalid left-hand side expression in postfix operation"], + "invalid_lhs_in_prefix_op", ["Invalid left-hand side expression in prefix operation"], + "multiple_defaults_in_switch", ["More than one default clause in switch statement"], + "newline_after_throw", ["Illegal newline after throw"], + "redeclaration", ["%0", " '", "%1", "' has already been declared"], + "no_catch_or_finally", ["Missing catch or finally after try"], + "unknown_label", ["Undefined label '", "%0", "'"], + "uncaught_exception", ["Uncaught ", "%0"], + "stack_trace", ["Stack Trace:\n", "%0"], + "called_non_callable", ["%0", " is not a function"], + "undefined_method", ["Object ", "%1", " has no method '", "%0", "'"], + "property_not_function", ["Property '", "%0", "' of object ", "%1", " is not a function"], + "cannot_convert_to_primitive", ["Cannot convert object to primitive value"], + "not_constructor", ["%0", " is not a constructor"], + "not_defined", ["%0", " is not defined"], + "non_object_property_load", ["Cannot read property '", "%0", "' of ", "%1"], + "non_object_property_store", ["Cannot set property '", "%0", "' of ", "%1"], + "non_object_property_call", ["Cannot call method '", "%0", "' of ", "%1"], + "with_expression", ["%0", " has no properties"], + "illegal_invocation", ["Illegal invocation"], + "no_setter_in_callback", ["Cannot set property ", "%0", " of ", "%1", " which has only a getter"], + "apply_non_function", ["Function.prototype.apply was called on ", "%0", ", which is a ", "%1", " and not a function"], + "apply_wrong_args", ["Function.prototype.apply: Arguments list has wrong type"], + "invalid_in_operator_use", ["Cannot use 'in' operator to search for '", "%0", "' in ", "%1"], + "instanceof_function_expected", ["Expecting a function in instanceof check, but got ", "%0"], + "instanceof_nonobject_proto", ["Function has non-object prototype '", "%0", "' in instanceof check"], + "null_to_object", ["Cannot convert null to object"], + "reduce_no_initial", ["Reduce of empty array with no initial value"], + "getter_must_be_callable", ["Getter must be a function: ", "%0"], + "setter_must_be_callable", ["Setter must be a function: ", "%0"], + "value_and_accessor", ["Invalid property. A property cannot both have accessors and be writable or have a value, ", "%0"], + "proto_object_or_null", ["Object prototype may only be an Object or null"], + "property_desc_object", ["Property description must be an object: ", "%0"], + "redefine_disallowed", ["Cannot redefine property: ", "%0"], + "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"], + "handler_returned_undefined", ["Proxy handler ", "%0", " returned undefined for '", "%1", "' trap"], + "proxy_prop_not_configurable", ["Trap ", "%1", " of proxy handler ", "%0", " returned non-configurable descriptor for property ", "%2"], + "proxy_non_object_prop_names", ["Trap ", "%1", " returned non-object ", "%0"], + "proxy_repeated_prop_name", ["Trap ", "%1", " returned repeated property name ", "%2"], + "invalid_weakmap_key", ["Invalid value used as weak map key"], // RangeError - invalid_array_length: ["Invalid array length"], - stack_overflow: ["Maximum call stack size exceeded"], + "invalid_array_length", ["Invalid array length"], + "stack_overflow", ["Maximum call stack size exceeded"], // SyntaxError - unable_to_parse: ["Parse error"], - invalid_regexp_flags: ["Invalid flags supplied to RegExp constructor '", "%0", "'"], - invalid_regexp: ["Invalid RegExp pattern /", "%0", "/"], - illegal_break: ["Illegal break statement"], - illegal_continue: ["Illegal continue statement"], - illegal_return: ["Illegal return statement"], - error_loading_debugger: ["Error loading debugger"], - no_input_to_regexp: ["No input to ", "%0"], - invalid_json: ["String '", "%0", "' is not valid JSON"], - circular_structure: ["Converting circular structure to JSON"], - obj_ctor_property_non_object: ["Object.", "%0", " called on non-object"], - called_on_null_or_undefined: ["%0", " called on null or undefined"], - array_indexof_not_defined: ["Array.getIndexOf: Argument undefined"], - object_not_extensible: ["Can't add property ", "%0", ", object is not extensible"], - illegal_access: ["Illegal access"], - invalid_preparser_data: ["Invalid preparser data for function ", "%0"], - strict_mode_with: ["Strict mode code may not include a with statement"], - strict_catch_variable: ["Catch variable may not be eval or arguments in strict mode"], - too_many_arguments: ["Too many arguments in function call (only 32766 allowed)"], - too_many_parameters: ["Too many parameters in function definition (only 32766 allowed)"], - too_many_variables: ["Too many variables declared (only 32767 allowed)"], - strict_param_name: ["Parameter name eval or arguments is not allowed in strict mode"], - strict_param_dupe: ["Strict mode function may not have duplicate parameter names"], - strict_var_name: ["Variable name may not be eval or arguments in strict mode"], - strict_function_name: ["Function name may not be eval or arguments in strict mode"], - strict_octal_literal: ["Octal literals are not allowed in strict mode."], - strict_duplicate_property: ["Duplicate data property in object literal not allowed in strict mode"], - accessor_data_property: ["Object literal may not have data and accessor property with the same name"], - accessor_get_set: ["Object literal may not have multiple get/set accessors with the same name"], - strict_lhs_assignment: ["Assignment to eval or arguments is not allowed in strict mode"], - strict_lhs_postfix: ["Postfix increment/decrement may not have eval or arguments operand in strict mode"], - strict_lhs_prefix: ["Prefix increment/decrement may not have eval or arguments operand in strict mode"], - strict_reserved_word: ["Use of future reserved word in strict mode"], - strict_delete: ["Delete of an unqualified identifier in strict mode."], - strict_delete_property: ["Cannot delete property '", "%0", "' of ", "%1"], - strict_const: ["Use of const in strict mode."], - strict_function: ["In strict mode code, functions can only be declared at top level or immediately within another function." ], - strict_read_only_property: ["Cannot assign to read only property '", "%0", "' of ", "%1"], - strict_cannot_assign: ["Cannot assign to read only '", "%0", "' in strict mode"], - strict_poison_pill: ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"], - strict_caller: ["Illegal access to a strict mode caller function."], - unprotected_let: ["Illegal let declaration in unprotected statement context."], - cant_prevent_ext_external_array_elements: ["Cannot prevent extension of an object with external array elements"], - redef_external_array_element: ["Cannot redefine a property of an object with external array elements"], - }; + "unable_to_parse", ["Parse error"], + "invalid_regexp_flags", ["Invalid flags supplied to RegExp constructor '", "%0", "'"], + "invalid_regexp", ["Invalid RegExp pattern /", "%0", "/"], + "illegal_break", ["Illegal break statement"], + "illegal_continue", ["Illegal continue statement"], + "illegal_return", ["Illegal return statement"], + "error_loading_debugger", ["Error loading debugger"], + "no_input_to_regexp", ["No input to ", "%0"], + "invalid_json", ["String '", "%0", "' is not valid JSON"], + "circular_structure", ["Converting circular structure to JSON"], + "obj_ctor_property_non_object", ["Object.", "%0", " called on non-object"], + "called_on_null_or_undefined", ["%0", " called on null or undefined"], + "array_indexof_not_defined", ["Array.getIndexOf: Argument undefined"], + "object_not_extensible", ["Can't add property ", "%0", ", object is not extensible"], + "illegal_access", ["Illegal access"], + "invalid_preparser_data", ["Invalid preparser data for function ", "%0"], + "strict_mode_with", ["Strict mode code may not include a with statement"], + "strict_catch_variable", ["Catch variable may not be eval or arguments in strict mode"], + "too_many_arguments", ["Too many arguments in function call (only 32766 allowed)"], + "too_many_parameters", ["Too many parameters in function definition (only 32766 allowed)"], + "too_many_variables", ["Too many variables declared (only 32767 allowed)"], + "strict_param_name", ["Parameter name eval or arguments is not allowed in strict mode"], + "strict_param_dupe", ["Strict mode function may not have duplicate parameter names"], + "strict_var_name", ["Variable name may not be eval or arguments in strict mode"], + "strict_function_name", ["Function name may not be eval or arguments in strict mode"], + "strict_octal_literal", ["Octal literals are not allowed in strict mode."], + "strict_duplicate_property", ["Duplicate data property in object literal not allowed in strict mode"], + "accessor_data_property", ["Object literal may not have data and accessor property with the same name"], + "accessor_get_set", ["Object literal may not have multiple get/set accessors with the same name"], + "strict_lhs_assignment", ["Assignment to eval or arguments is not allowed in strict mode"], + "strict_lhs_postfix", ["Postfix increment/decrement may not have eval or arguments operand in strict mode"], + "strict_lhs_prefix", ["Prefix increment/decrement may not have eval or arguments operand in strict mode"], + "strict_reserved_word", ["Use of future reserved word in strict mode"], + "strict_delete", ["Delete of an unqualified identifier in strict mode."], + "strict_delete_property", ["Cannot delete property '", "%0", "' of ", "%1"], + "strict_const", ["Use of const in strict mode."], + "strict_function", ["In strict mode code, functions can only be declared at top level or immediately within another function." ], + "strict_read_only_property", ["Cannot assign to read only property '", "%0", "' of ", "%1"], + "strict_cannot_assign", ["Cannot assign to read only '", "%0", "' in strict mode"], + "strict_poison_pill", ["'caller', 'callee', and 'arguments' properties may not be accessed on strict mode functions or the arguments objects for calls to them"], + "strict_caller", ["Illegal access to a strict mode caller function."], + "unprotected_let", ["Illegal let declaration in unprotected statement context."], + "cant_prevent_ext_external_array_elements", ["Cannot prevent extension of an object with external array elements"], + "redef_external_array_element", ["Cannot redefine a property of an object with external array elements"], + ]; + var messages = { __proto__ : null }; + var desc = new PropertyDescriptor(); + desc.setConfigurable(false); + desc.setEnumerable(false); + desc.setWritable(false); + for (var i = 0; i < messagesDictionary.length; i += 2) { + var key = messagesDictionary[i]; + var format = messagesDictionary[i + 1]; + ObjectFreeze(format); + desc.setValue(format); + DefineOwnProperty(messages, key, desc); + } + %PreventExtensions(messages); + %IgnoreAttributesAndSetProperty(builtins, "kMessages", + messages, + DONT_DELETE | DONT_ENUM | READ_ONLY); } var message_type = %MessageGetType(message); var format = kMessages[message_type]; @@ -319,7 +324,7 @@ function MakeError(type, args) { * @return {number} 0 if input too small, -1 if input too large, else the line number. */ -Script.prototype.lineFromPosition = function(position) { +function ScriptLineFromPosition(position) { var lower = 0; var upper = this.lineCount() - 1; var line_ends = this.line_ends; @@ -358,8 +363,8 @@ Script.prototype.lineFromPosition = function(position) { * @return {SourceLocation} * If line is negative or not in the source null is returned. */ -Script.prototype.locationFromPosition = function (position, - include_resource_offset) { +function ScriptLocationFromPosition(position, + include_resource_offset) { var line = this.lineFromPosition(position); if (line == -1) return null; @@ -367,7 +372,9 @@ Script.prototype.locationFromPosition = function (position, var line_ends = this.line_ends; var start = line == 0 ? 0 : line_ends[line - 1] + 1; var end = line_ends[line]; - if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') end--; + if (end > 0 && %_CallFunction(this.source, end - 1, StringCharAt) == '\r') { + end--; + } var column = position - start; // Adjust according to the offset within the resource. @@ -392,11 +399,12 @@ Script.prototype.locationFromPosition = function (position, * @param {number} opt_line The line within the source. Default value is 0 * @param {number} opt_column The column in within the line. Default value is 0 * @param {number} opt_offset_position The offset from the begining of the - * source from where the line and column calculation starts. Default value is 0 + * source from where the line and column calculation starts. + * Default value is 0 * @return {SourceLocation} * If line is negative or not in the source null is returned. */ -Script.prototype.locationFromLine = function (opt_line, opt_column, opt_offset_position) { +function ScriptLocationFromLine(opt_line, opt_column, opt_offset_position) { // Default is the first line in the script. Lines in the script is relative // to the offset within the resource. var line = 0; @@ -438,7 +446,7 @@ Script.prototype.locationFromLine = function (opt_line, opt_column, opt_offset_p * @return {SourceSlice} The source slice or null of the parameters where * invalid */ -Script.prototype.sourceSlice = function (opt_from_line, opt_to_line) { +function ScriptSourceSlice(opt_from_line, opt_to_line) { var from_line = IS_UNDEFINED(opt_from_line) ? this.line_offset : opt_from_line; var to_line = IS_UNDEFINED(opt_to_line) ? this.line_offset + this.lineCount() : opt_to_line @@ -465,7 +473,7 @@ Script.prototype.sourceSlice = function (opt_from_line, opt_to_line) { } -Script.prototype.sourceLine = function (opt_line) { +function ScriptSourceLine(opt_line) { // Default is the first line in the script. Lines in the script are relative // to the offset within the resource. var line = 0; @@ -491,7 +499,7 @@ Script.prototype.sourceLine = function (opt_line) { * @return {number} * Number of source lines. */ -Script.prototype.lineCount = function() { +function ScriptLineCount() { // Return number of source lines. return this.line_ends.length; }; @@ -507,9 +515,10 @@ Script.prototype.lineCount = function() { * @return {?string} script name if present, value for //@ sourceURL comment * otherwise. */ -Script.prototype.nameOrSourceURL = function() { - if (this.name) +function ScriptNameOrSourceURL() { + if (this.name) { return this.name; + } // TODO(608): the spaces in a regexp below had to be escaped as \040 // because this file is being processed by js2c whose handling of spaces // in regexps is broken. Also, ['"] are excluded from allowed URLs to @@ -535,6 +544,20 @@ Script.prototype.nameOrSourceURL = function() { } +SetUpLockedPrototype(Script, + $Array("source", "name", "line_ends", "line_offset", "column_offset"), + $Array( + "lineFromPosition", ScriptLineFromPosition, + "locationFromPosition", ScriptLocationFromPosition, + "locationFromLine", ScriptLocationFromLine, + "sourceSlice", ScriptSourceSlice, + "sourceLine", ScriptSourceLine, + "lineCount", ScriptLineCount, + "nameOrSourceURL", ScriptNameOrSourceURL + ) +); + + /** * Class for source location. A source location is a position within some * source with the following properties: @@ -565,8 +588,6 @@ function SourceLocation(script, position, line, column, start, end) { this.end = end; } -SourceLocation.prototype.__proto__ = null; - const kLineLengthLimit = 78; /** @@ -577,7 +598,7 @@ const kLineLengthLimit = 78; * @param {number} opt_before The number of characters to prefer before the * position with a default value of 10 less that the limit */ -SourceLocation.prototype.restrict = function (opt_limit, opt_before) { +function SourceLocationRestrict(opt_limit, opt_before) { // Find the actual limit to use. var limit; var before; @@ -624,11 +645,20 @@ SourceLocation.prototype.restrict = function (opt_limit, opt_before) { * @return {String} * Source text for this location. */ -SourceLocation.prototype.sourceText = function () { +function SourceLocationSourceText() { return %_CallFunction(this.script.source, this.start, this.end, StringSubstring); }; +SetUpLockedPrototype(SourceLocation, + $Array("script", "position", "line", "column", "start", "end"), + $Array( + "restrict", SourceLocationRestrict, + "sourceText", SourceLocationSourceText + ) +); + + /** * Class for a source slice. A source slice is a part of a script source with * the following properties: @@ -655,20 +685,23 @@ function SourceSlice(script, from_line, to_line, from_position, to_position) { this.to_position = to_position; } -SourceSlice.prototype.__proto__ = null; - /** * Get the source text for a SourceSlice * @return {String} Source text for this slice. The last line will include * the line terminating characters (if any) */ -SourceSlice.prototype.sourceText = function () { +function SourceSliceSourceText() { return %_CallFunction(this.script.source, this.from_position, this.to_position, StringSubstring); }; +SetUpLockedPrototype(SourceSlice, + $Array("script", "from_line", "to_line", "from_position", "to_position"), + $Array("sourceText", SourceSliceSourceText) +); + // Returns the offset of the given position within the containing // line. @@ -723,13 +756,11 @@ function CallSite(receiver, fun, pos) { this.pos = pos; } -CallSite.prototype.__proto__ = null; - -CallSite.prototype.getThis = function () { +function CallSiteGetThis() { return this.receiver; }; -CallSite.prototype.getTypeName = function () { +function CallSiteGetTypeName() { var constructor = this.receiver.constructor; if (!constructor) { return %_CallFunction(this.receiver, ObjectToString); @@ -741,33 +772,33 @@ CallSite.prototype.getTypeName = function () { return constructorName; }; -CallSite.prototype.isToplevel = function () { +function CallSiteIsToplevel() { if (this.receiver == null) { return true; } return IS_GLOBAL(this.receiver); }; -CallSite.prototype.isEval = function () { +function CallSiteIsEval() { var script = %FunctionGetScript(this.fun); return script && script.compilation_type == COMPILATION_TYPE_EVAL; }; -CallSite.prototype.getEvalOrigin = function () { +function CallSiteGetEvalOrigin() { var script = %FunctionGetScript(this.fun); return FormatEvalOrigin(script); }; -CallSite.prototype.getScriptNameOrSourceURL = function () { +function CallSiteGetScriptNameOrSourceURL() { var script = %FunctionGetScript(this.fun); return script ? script.nameOrSourceURL() : null; }; -CallSite.prototype.getFunction = function () { +function CallSiteGetFunction() { return this.fun; }; -CallSite.prototype.getFunctionName = function () { +function CallSiteGetFunctionName() { // See if the function knows its own name var name = this.fun.name; if (name) { @@ -783,7 +814,7 @@ CallSite.prototype.getFunctionName = function () { return null; }; -CallSite.prototype.getMethodName = function () { +function CallSiteGetMethodName() { // See if we can find a unique property on the receiver that holds // this function. var ownName = this.fun.name; @@ -813,12 +844,12 @@ CallSite.prototype.getMethodName = function () { return null; }; -CallSite.prototype.getFileName = function () { +function CallSiteGetFileName() { var script = %FunctionGetScript(this.fun); return script ? script.name : null; }; -CallSite.prototype.getLineNumber = function () { +function CallSiteGetLineNumber() { if (this.pos == -1) { return null; } @@ -830,7 +861,7 @@ CallSite.prototype.getLineNumber = function () { return location ? location.line + 1 : null; }; -CallSite.prototype.getColumnNumber = function () { +function CallSiteGetColumnNumber() { if (this.pos == -1) { return null; } @@ -842,16 +873,16 @@ CallSite.prototype.getColumnNumber = function () { return location ? location.column + 1: null; }; -CallSite.prototype.isNative = function () { +function CallSiteIsNative() { var script = %FunctionGetScript(this.fun); return script ? (script.type == TYPE_NATIVE) : false; }; -CallSite.prototype.getPosition = function () { +function CallSiteGetPosition() { return this.pos; }; -CallSite.prototype.isConstructor = function () { +function CallSiteIsConstructor() { var constructor = this.receiver ? this.receiver.constructor : null; if (!constructor) { return false; @@ -859,6 +890,25 @@ CallSite.prototype.isConstructor = function () { return this.fun === constructor; }; +SetUpLockedPrototype(CallSite, $Array("receiver", "fun", "pos"), $Array( + "getThis", CallSiteGetThis, + "getTypeName", CallSiteGetTypeName, + "isToplevel", CallSiteIsToplevel, + "isEval", CallSiteIsEval, + "getEvalOrigin", CallSiteGetEvalOrigin, + "getScriptNameOrSourceURL", CallSiteGetScriptNameOrSourceURL, + "getFunction", CallSiteGetFunction, + "getFunctionName", CallSiteGetFunctionName, + "getMethodName", CallSiteGetMethodName, + "getFileName", CallSiteGetFileName, + "getLineNumber", CallSiteGetLineNumber, + "getColumnNumber", CallSiteGetColumnNumber, + "isNative", CallSiteIsNative, + "getPosition", CallSiteGetPosition, + "isConstructor", CallSiteIsConstructor +)); + + function FormatEvalOrigin(script) { var sourceURL = script.nameOrSourceURL(); if (sourceURL) { @@ -1000,63 +1050,6 @@ function FormatRawStackTrace(error, raw_stack) { } } -function DefineError(f) { - // Store the error function in both the global object - // and the runtime object. The function is fetched - // from the runtime object when throwing errors from - // within the runtime system to avoid strange side - // effects when overwriting the error functions from - // user code. - var name = f.name; - %SetProperty(global, name, f, DONT_ENUM); - this['$' + name] = f; - // Configure the error function. - if (name == 'Error') { - // The prototype of the Error object must itself be an error. - // However, it can't be an instance of the Error object because - // it hasn't been properly configured yet. Instead we create a - // special not-a-true-error-but-close-enough object. - function ErrorPrototype() {} - %FunctionSetPrototype(ErrorPrototype, $Object.prototype); - %FunctionSetInstanceClassName(ErrorPrototype, 'Error'); - %FunctionSetPrototype(f, new ErrorPrototype()); - } else { - %FunctionSetPrototype(f, new $Error()); - } - %FunctionSetInstanceClassName(f, 'Error'); - %SetProperty(f.prototype, 'constructor', f, DONT_ENUM); - // The name property on the prototype of error objects is not - // specified as being read-one and dont-delete. However, allowing - // overwriting allows leaks of error objects between script blocks - // in the same context in a browser setting. Therefore we fix the - // name. - %SetProperty(f.prototype, "name", name, DONT_ENUM | DONT_DELETE | READ_ONLY); - %SetCode(f, function(m) { - if (%_IsConstructCall()) { - // Define all the expected properties directly on the error - // object. This avoids going through getters and setters defined - // on prototype objects. - %IgnoreAttributesAndSetProperty(this, 'stack', void 0, DONT_ENUM); - %IgnoreAttributesAndSetProperty(this, 'arguments', void 0, DONT_ENUM); - %IgnoreAttributesAndSetProperty(this, 'type', void 0, DONT_ENUM); - if (m === kAddMessageAccessorsMarker) { - // DefineOneShotAccessor always inserts a message property and - // ignores setters. - DefineOneShotAccessor(this, 'message', function (obj) { - return FormatMessage(%NewMessageObject(obj.type, obj.arguments)); - }); - } else if (!IS_UNDEFINED(m)) { - %IgnoreAttributesAndSetProperty(this, - 'message', - ToString(m), - DONT_ENUM); - } - captureStackTrace(this, f); - } else { - return new f(m); - } - }); -} function captureStackTrace(obj, cons_opt) { var stackTraceLimit = $Error.stackTraceLimit; @@ -1072,52 +1065,100 @@ function captureStackTrace(obj, cons_opt) { }); }; -$Math.__proto__ = global.Object.prototype; -// DefineError is a native function. Use explicit receiver. Otherwise -// the receiver will be 'undefined'. -this.DefineError(function Error() { }); -this.DefineError(function TypeError() { }); -this.DefineError(function RangeError() { }); -this.DefineError(function SyntaxError() { }); -this.DefineError(function ReferenceError() { }); -this.DefineError(function EvalError() { }); -this.DefineError(function URIError() { }); +function SetUpError() { + // Define special error type constructors. + + function DefineError(f) { + // Store the error function in both the global object + // and the runtime object. The function is fetched + // from the runtime object when throwing errors from + // within the runtime system to avoid strange side + // effects when overwriting the error functions from + // user code. + var name = f.name; + %SetProperty(global, name, f, DONT_ENUM); + %SetProperty(builtins, '$' + name, f, DONT_ENUM | DONT_DELETE | READ_ONLY); + // Configure the error function. + if (name == 'Error') { + // The prototype of the Error object must itself be an error. + // However, it can't be an instance of the Error object because + // it hasn't been properly configured yet. Instead we create a + // special not-a-true-error-but-close-enough object. + function ErrorPrototype() {} + %FunctionSetPrototype(ErrorPrototype, $Object.prototype); + %FunctionSetInstanceClassName(ErrorPrototype, 'Error'); + %FunctionSetPrototype(f, new ErrorPrototype()); + } else { + %FunctionSetPrototype(f, new $Error()); + } + %FunctionSetInstanceClassName(f, 'Error'); + %SetProperty(f.prototype, 'constructor', f, DONT_ENUM); + // The name property on the prototype of error objects is not + // specified as being read-one and dont-delete. However, allowing + // overwriting allows leaks of error objects between script blocks + // in the same context in a browser setting. Therefore we fix the + // name. + %SetProperty(f.prototype, "name", name, + DONT_ENUM | DONT_DELETE | READ_ONLY) ; + %SetCode(f, function(m) { + if (%_IsConstructCall()) { + // Define all the expected properties directly on the error + // object. This avoids going through getters and setters defined + // on prototype objects. + %IgnoreAttributesAndSetProperty(this, 'stack', void 0, DONT_ENUM); + %IgnoreAttributesAndSetProperty(this, 'arguments', void 0, DONT_ENUM); + %IgnoreAttributesAndSetProperty(this, 'type', void 0, DONT_ENUM); + if (m === kAddMessageAccessorsMarker) { + // DefineOneShotAccessor always inserts a message property and + // ignores setters. + DefineOneShotAccessor(this, 'message', function (obj) { + return FormatMessage(%NewMessageObject(obj.type, obj.arguments)); + }); + } else if (!IS_UNDEFINED(m)) { + %IgnoreAttributesAndSetProperty(this, + 'message', + ToString(m), + DONT_ENUM); + } + captureStackTrace(this, f); + } else { + return new f(m); + } + }); + } -$Error.captureStackTrace = captureStackTrace; + DefineError(function Error() { }); + DefineError(function TypeError() { }); + DefineError(function RangeError() { }); + DefineError(function SyntaxError() { }); + DefineError(function ReferenceError() { }); + DefineError(function EvalError() { }); + DefineError(function URIError() { }); +} -// Setup extra properties of the Error.prototype object. -function setErrorMessage() { - var desc = {value: '', - enumerable: false, - configurable: true, - writable: true }; - DefineOwnProperty($Error.prototype, - 'message', - ToPropertyDescriptor(desc), - true); +SetUpError(); -} +$Error.captureStackTrace = captureStackTrace; -setErrorMessage(); +%SetProperty($Error.prototype, 'message', '', DONT_ENUM); // Global list of error objects visited during errorToString. This is // used to detect cycles in error toString formatting. -var visited_errors = new $Array(); -var cyclic_error_marker = new $Object(); +const visited_errors = new InternalArray(); +const cyclic_error_marker = new $Object(); -function errorToStringDetectCycle() { - if (!%PushIfAbsent(visited_errors, this)) throw cyclic_error_marker; +function errorToStringDetectCycle(error) { + if (!%PushIfAbsent(visited_errors, error)) throw cyclic_error_marker; try { - var type = this.type; - if (type && !%_CallFunction(this, "message", ObjectHasOwnProperty)) { - var formatted = FormatMessage(%NewMessageObject(type, this.arguments)); - return this.name + ": " + formatted; + var type = error.type; + var hasMessage = %_CallFunction(error, "message", ObjectHasOwnProperty); + if (type && !hasMessage) { + var formatted = FormatMessage(%NewMessageObject(type, error.arguments)); + return error.name + ": " + formatted; } - var message = %_CallFunction(this, "message", ObjectHasOwnProperty) - ? (": " + this.message) - : ""; - return this.name + message; + var message = hasMessage ? (": " + error.message) : ""; + return error.name + message; } finally { visited_errors.length = visited_errors.length - 1; } @@ -1133,7 +1174,7 @@ function errorToString() { function isCyclicErrorMarker(o) { return o === cyclic_error_marker; } try { - return %_CallFunction(this, errorToStringDetectCycle); + return errorToStringDetectCycle(this); } catch(e) { // If this error message was encountered already return the empty // string for it instead of recursively formatting it. diff --git a/src/mips/assembler-mips-inl.h b/src/mips/assembler-mips-inl.h index b5ffe739..c4c4fd25 100644 --- a/src/mips/assembler-mips-inl.h +++ b/src/mips/assembler-mips-inl.h @@ -83,6 +83,14 @@ bool Operand::is_reg() const { // 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 @@ bool RelocInfo::IsPatchedReturnSequence() { 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 28ac5577..e01a0ca7 100644 --- a/src/mips/assembler-mips.cc +++ b/src/mips/assembler-mips.cc @@ -49,11 +49,47 @@ bool CpuFeatures::initialized_ = false; unsigned CpuFeatures::supported_ = 0; unsigned CpuFeatures::found_by_runtime_probing_ = 0; + +// Get the CPU features enabled by the build. For cross compilation the +// preprocessor symbols CAN_USE_FPU_INSTRUCTIONS +// can be defined to enable FPU instructions when building the +// snapshot. +static uint64_t CpuFeaturesImpliedByCompiler() { + uint64_t answer = 0; +#ifdef CAN_USE_FPU_INSTRUCTIONS + answer |= 1u << FPU; +#endif // def CAN_USE_FPU_INSTRUCTIONS + +#ifdef __mips__ + // If the compiler is allowed to use FPU then we can use FPU too in our code + // generation even when generating snapshots. This won't work for cross + // compilation. +#if(defined(__mips_hard_float) && __mips_hard_float != 0) + answer |= 1u << FPU; +#endif // defined(__mips_hard_float) && __mips_hard_float != 0 +#endif // def __mips__ + + return answer; +} + + void CpuFeatures::Probe() { ASSERT(!initialized_); #ifdef DEBUG initialized_ = true; #endif + + // Get the features implied by the OS and the compiler settings. This is the + // minimal set of features which is also allowed for generated code in the + // snapshot. + supported_ |= OS::CpuFeaturesImpliedByPlatform(); + supported_ |= CpuFeaturesImpliedByCompiler(); + + if (Serializer::enabled()) { + // No probing for features if we might serialize (generate snapshot). + return; + } + // If the compiler is allowed to use fpu then we can use fpu too in our // code generation. #if !defined(__mips__) @@ -62,11 +98,7 @@ void CpuFeatures::Probe() { supported_ |= 1u << FPU; } #else - if (Serializer::enabled()) { - supported_ |= OS::CpuFeaturesImpliedByPlatform(); - return; // No features if we might serialize. - } - + // Probe for additional features not already known to be available. if (OS::MipsCpuHasFeature(FPU)) { // This implementation also sets the FPU flags if // runtime detection of FPU returns true. @@ -140,7 +172,8 @@ Register ToRegister(int num) { // ----------------------------------------------------------------------------- // Implementation of RelocInfo. -const int RelocInfo::kApplyMask = 1 << RelocInfo::INTERNAL_REFERENCE; +const int RelocInfo::kApplyMask = RelocInfo::kCodeTargetMask | + 1 << RelocInfo::INTERNAL_REFERENCE; bool RelocInfo::IsCodedSpecially() { @@ -514,6 +547,19 @@ bool Assembler::IsJ(Instr instr) { } +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. @@ -907,7 +953,7 @@ void Assembler::GenInstrImmediate(Opcode opcode, void Assembler::GenInstrJump(Opcode opcode, - uint32_t address) { + uint32_t address) { BlockTrampolinePoolScope block_trampoline_pool(this); ASSERT(is_uint26(address)); Instr instr = opcode | address; @@ -1080,7 +1126,12 @@ void Assembler::bne(Register rs, Register rt, int16_t offset) { 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); } @@ -1096,8 +1147,13 @@ void Assembler::jr(Register rs) { 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); } @@ -1110,6 +1166,32 @@ void Assembler::jalr(Register rs, Register rd) { } +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. @@ -1582,6 +1664,13 @@ void Assembler::cfc1(Register rt, FPUControlRegister fs) { 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. @@ -1940,10 +2029,15 @@ void Assembler::RecordRelocInfo(RelocInfo::Mode rmode, intptr_t data) { } 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) { @@ -2038,30 +2132,142 @@ Address Assembler::target_address_at(Address pc) { } +// 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); + uint32_t rt_code = GetRtField(instr2); + uint32_t* p = reinterpret_cast<uint32_t*>(pc); + uint32_t itarget = reinterpret_cast<uint32_t>(target); + #ifdef DEBUG + // Check we have the result from a li macro-instruction, using instr pair. 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. + // 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 e5077bee..38e9537a 100644 --- a/src/mips/assembler-mips.h +++ b/src/mips/assembler-mips.h @@ -168,24 +168,36 @@ Register ToRegister(int num); // 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 @@ struct FPURegister { "f20", "f22", "f24", - "f26", - "f28", - "f30" + "f26" }; return names[index]; } @@ -212,6 +222,23 @@ struct FPURegister { 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 @@ struct FPURegister { int code_; }; +// 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.) + +// For O32 ABI, Floats and Doubles refer to same set of 32 32-bit registers. typedef FPURegister DoubleRegister; +typedef FPURegister FloatRegister; -const FPURegister no_creg = { -1 }; +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 f29 = { 29 }; 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 @@ class MemOperand : public Operand { 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 @@ class Assembler : public AssemblerBase { 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 @@ class Assembler : public AssemblerBase { 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 @@ class Assembler : public AssemblerBase { 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 @@ class Assembler : public AssemblerBase { 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 @@ class Assembler : public AssemblerBase { return internal_trampoline_exception_; } + void DoubleAsTwoUInt32(double d, uint32_t* lo, uint32_t* hi); + bool is_trampoline_emitted() const { return trampoline_emitted_; } @@ -1159,6 +1216,7 @@ class Assembler : public AssemblerBase { } return trampoline_slot; } + private: int start_; int end_; diff --git a/src/mips/builtins-mips.cc b/src/mips/builtins-mips.cc index 1555653f..d7723044 100644 --- a/src/mips/builtins-mips.cc +++ b/src/mips/builtins-mips.cc @@ -210,7 +210,7 @@ static void AllocateJSArray(MacroAssembler* masm, // Allocate the JSArray object together with space for a FixedArray with the // requested number of elements. __ bind(¬_empty); - ASSERT(kSmiTagSize == 1 && kSmiTag == 0); + STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); __ li(elements_array_end, (JSArray::kSize + FixedArray::kHeaderSize) / kPointerSize); __ sra(scratch1, array_size, kSmiTagSize); @@ -261,7 +261,7 @@ static void AllocateJSArray(MacroAssembler* masm, // Length of the FixedArray is the number of pre-allocated elements if // the actual JSArray has length 0 and the size of the JSArray for non-empty // JSArrays. The length of a FixedArray is stored as a smi. - ASSERT(kSmiTag == 0); + STATIC_ASSERT(kSmiTag == 0); __ li(at, Operand(Smi::FromInt(JSArray::kPreallocatedArrayElements))); __ movz(array_size, at, array_size); @@ -273,7 +273,7 @@ static void AllocateJSArray(MacroAssembler* masm, // result: JSObject // elements_array_storage: elements array element storage // array_size: smi-tagged size of elements array - ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); __ sll(elements_array_end, array_size, kPointerSizeLog2 - kSmiTagSize); __ Addu(elements_array_end, elements_array_storage, elements_array_end); @@ -336,14 +336,14 @@ static void ArrayNativeCode(MacroAssembler* masm, __ bind(&argc_one_or_more); __ Branch(&argc_two_or_more, ne, a0, Operand(1)); - ASSERT(kSmiTag == 0); + STATIC_ASSERT(kSmiTag == 0); __ lw(a2, MemOperand(sp)); // Get the argument from the stack. __ And(a3, a2, Operand(kIntptrSignBit | kSmiTagMask)); __ Branch(call_generic_code, eq, a3, Operand(zero_reg)); // Handle construction of an empty array of a certain size. Bail out if size // is too large to actually allocate an elements array. - ASSERT(kSmiTag == 0); + STATIC_ASSERT(kSmiTag == 0); __ Branch(call_generic_code, Ugreater_equal, a2, Operand(JSObject::kInitialMaxFastElementArray << kSmiTagSize)); @@ -576,7 +576,7 @@ void Builtins::Generate_StringConstructCode(MacroAssembler* masm) { // Is it a String? __ lw(a2, FieldMemOperand(a0, HeapObject::kMapOffset)); __ lbu(a3, FieldMemOperand(a2, Map::kInstanceTypeOffset)); - ASSERT(kNotStringTag != 0); + STATIC_ASSERT(kNotStringTag != 0); __ And(t0, a3, Operand(kIsNotStringMask)); __ Branch(&convert_argument, ne, t0, Operand(zero_reg)); __ mov(argument, a0); diff --git a/src/mips/code-stubs-mips.cc b/src/mips/code-stubs-mips.cc index 9385f2fd..521b8e58 100644 --- a/src/mips/code-stubs-mips.cc +++ b/src/mips/code-stubs-mips.cc @@ -3538,7 +3538,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, const int kNumInstructionsToJump = 6; masm->Addu(ra, ra, kNumInstructionsToJump * kPointerSize); masm->sw(ra, MemOperand(sp)); // This spot was reserved in EnterExitFrame. - masm->Subu(sp, sp, StandardFrameConstants::kCArgsSlotsSize); + masm->Subu(sp, sp, kCArgsSlotsSize); // Stack is still aligned. // Call the C routine. @@ -3551,7 +3551,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, } // Restore stack (remove arg slots). - __ Addu(sp, sp, StandardFrameConstants::kCArgsSlotsSize); + __ Addu(sp, sp, kCArgsSlotsSize); if (always_allocate) { // It's okay to clobber a2 and a3 here. v0 & v1 contain result. @@ -3591,7 +3591,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, __ li(t0, Operand(ExternalReference::the_hole_value_location(masm->isolate()))); __ lw(a3, MemOperand(t0)); - __ li(t0, Operand(ExternalReference(Isolate::k_pending_exception_address, + __ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress, masm->isolate()))); __ lw(v0, MemOperand(t0)); __ sw(a3, MemOperand(t0)); @@ -3695,16 +3695,26 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Save callee saved registers on the stack. __ MultiPush(kCalleeSaved | ra.bit()); + if (CpuFeatures::IsSupported(FPU)) { + CpuFeatures::Scope scope(FPU); + // Save callee-saved FPU registers. + __ MultiPushFPU(kCalleeSavedFPU); + } + // Load argv in s0 register. - __ lw(s0, MemOperand(sp, (kNumCalleeSaved + 1) * kPointerSize + - StandardFrameConstants::kCArgsSlotsSize)); + int offset_to_argv = (kNumCalleeSaved + 1) * kPointerSize; + if (CpuFeatures::IsSupported(FPU)) { + offset_to_argv += kNumCalleeSavedFPU * kDoubleSize; + } + + __ lw(s0, MemOperand(sp, offset_to_argv + kCArgsSlotsSize)); // We build an EntryFrame. __ li(t3, Operand(-1)); // Push a bad frame pointer to fail if it is used. int marker = is_construct ? StackFrame::ENTRY_CONSTRUCT : StackFrame::ENTRY; __ li(t2, Operand(Smi::FromInt(marker))); __ li(t1, Operand(Smi::FromInt(marker))); - __ li(t0, Operand(ExternalReference(Isolate::k_c_entry_fp_address, + __ li(t0, Operand(ExternalReference(Isolate::kCEntryFPAddress, masm->isolate()))); __ lw(t0, MemOperand(t0)); __ Push(t3, t2, t1, t0); @@ -3729,7 +3739,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // If this is the outermost JS call, set js_entry_sp value. Label non_outermost_js; - ExternalReference js_entry_sp(Isolate::k_js_entry_sp_address, + ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress, masm->isolate()); __ li(t1, Operand(ExternalReference(js_entry_sp))); __ lw(t2, MemOperand(t1)); @@ -3752,7 +3762,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // exception field in the JSEnv and return a failure sentinel. // Coming in here the fp will be invalid because the PushTryHandler below // sets it to 0 to signal the existence of the JSEntry frame. - __ li(t0, Operand(ExternalReference(Isolate::k_pending_exception_address, + __ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress, masm->isolate()))); __ sw(v0, MemOperand(t0)); // We come back from 'invoke'. result is in v0. __ li(v0, Operand(reinterpret_cast<int32_t>(Failure::Exception()))); @@ -3771,7 +3781,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { __ li(t0, Operand(ExternalReference::the_hole_value_location(masm->isolate()))); __ lw(t1, MemOperand(t0)); - __ li(t0, Operand(ExternalReference(Isolate::k_pending_exception_address, + __ li(t0, Operand(ExternalReference(Isolate::kPendingExceptionAddress, masm->isolate()))); __ sw(t1, MemOperand(t0)); @@ -3822,13 +3832,19 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Restore the top frame descriptors from the stack. __ pop(t1); - __ li(t0, Operand(ExternalReference(Isolate::k_c_entry_fp_address, + __ li(t0, Operand(ExternalReference(Isolate::kCEntryFPAddress, masm->isolate()))); __ sw(t1, MemOperand(t0)); // Reset the stack to the callee saved registers. __ addiu(sp, sp, -EntryFrameConstants::kCallerFPOffset); + if (CpuFeatures::IsSupported(FPU)) { + CpuFeatures::Scope scope(FPU); + // Restore callee-saved fpu registers. + __ MultiPopFPU(kCalleeSavedFPU); + } + // Restore callee saved registers from the stack. __ MultiPop(kCalleeSaved | ra.bit()); // Return. @@ -4652,7 +4668,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // frame. Therefore we have to use fp, which points exactly to two pointer // sizes below the previous sp. (Because creating a new stack frame pushes // the previous fp onto the stack and moves up sp by 2 * kPointerSize.) - __ lw(a0, MemOperand(fp, kSubjectOffset + 2 * kPointerSize)); + __ lw(subject, MemOperand(fp, kSubjectOffset + 2 * kPointerSize)); // If slice offset is not 0, load the length from the original sliced string. // Argument 4, a3: End of string data // Argument 3, a2: Start of string data @@ -4662,7 +4678,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ sllv(t1, a1, a3); __ addu(a2, t0, t1); - __ lw(t2, FieldMemOperand(a0, String::kLengthOffset)); + __ lw(t2, FieldMemOperand(subject, String::kLengthOffset)); __ sra(t2, t2, kSmiTagSize); __ sllv(t1, t2, a3); __ addu(a3, t0, t1); @@ -4670,7 +4686,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // Already there // Argument 1 (a0): Subject string. - // Already there + __ mov(a0, subject); // Locate the code entry and call it. __ Addu(t9, t9, Operand(Code::kHeaderSize - kHeapObjectTag)); @@ -4688,13 +4704,13 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { Label success; __ Branch(&success, eq, - subject, Operand(NativeRegExpMacroAssembler::SUCCESS)); + v0, Operand(NativeRegExpMacroAssembler::SUCCESS)); Label failure; __ Branch(&failure, eq, - subject, Operand(NativeRegExpMacroAssembler::FAILURE)); + v0, Operand(NativeRegExpMacroAssembler::FAILURE)); // If not exception it can only be retry. Handle that in the runtime system. __ Branch(&runtime, ne, - subject, Operand(NativeRegExpMacroAssembler::EXCEPTION)); + v0, Operand(NativeRegExpMacroAssembler::EXCEPTION)); // Result must now be exception. If there is no pending exception already a // stack overflow (on the backtrack stack) was detected in RegExp code but // haven't created the exception yet. Handle that in the runtime system. @@ -4702,19 +4718,19 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { __ li(a1, Operand( ExternalReference::the_hole_value_location(masm->isolate()))); __ lw(a1, MemOperand(a1, 0)); - __ li(a2, Operand(ExternalReference(Isolate::k_pending_exception_address, + __ li(a2, Operand(ExternalReference(Isolate::kPendingExceptionAddress, masm->isolate()))); __ lw(v0, MemOperand(a2, 0)); - __ Branch(&runtime, eq, subject, Operand(a1)); + __ Branch(&runtime, eq, v0, Operand(a1)); __ sw(a1, MemOperand(a2, 0)); // Clear pending exception. // Check if the exception is a termination. If so, throw as uncatchable. __ LoadRoot(a0, Heap::kTerminationExceptionRootIndex); Label termination_exception; - __ Branch(&termination_exception, eq, subject, Operand(a0)); + __ Branch(&termination_exception, eq, v0, Operand(a0)); - __ Throw(subject); // Expects thrown value in v0. + __ Throw(v0); // Expects thrown value in v0. __ bind(&termination_exception); __ ThrowUncatchable(TERMINATION, v0); // Expects thrown value in v0. @@ -5062,7 +5078,8 @@ void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { // Check for 1-byte or 2-byte string. __ bind(&flat_string); - STATIC_ASSERT(kAsciiStringTag != 0); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ And(t0, result_, Operand(kStringEncodingMask)); __ Branch(&ascii_string, ne, t0, Operand(zero_reg)); @@ -5625,11 +5642,6 @@ void SubStringStub::Generate(MacroAssembler* masm) { Register to = t2; Register from = t3; - if (FLAG_string_slices) { - __ nop(); // Jumping as first instruction would crash the code generation. - __ jmp(&sub_string_runtime); - } - // Check bounds and smi-ness. __ lw(to, MemOperand(sp, kToOffset)); __ lw(from, MemOperand(sp, kFromOffset)); @@ -5653,7 +5665,8 @@ void SubStringStub::Generate(MacroAssembler* masm) { // Special handling of sub-strings of length 1 and 2. One character strings // are handled in the runtime system (looked up in the single character - // cache). Two character strings are looked for in the symbol cache. + // cache). Two character strings are looked for in the symbol cache in + // generated code. __ Branch(&sub_string_runtime, lt, a2, Operand(2)); // Both to and from are smis. @@ -5665,19 +5678,32 @@ void SubStringStub::Generate(MacroAssembler* masm) { // t5: to index (untagged smi) // Make sure first argument is a sequential (or flat) string. - __ lw(t1, MemOperand(sp, kStringOffset)); - __ Branch(&sub_string_runtime, eq, t1, Operand(kSmiTagMask)); + __ lw(v0, MemOperand(sp, kStringOffset)); + __ Branch(&sub_string_runtime, eq, v0, Operand(kSmiTagMask)); - __ lw(a1, FieldMemOperand(t1, HeapObject::kMapOffset)); + __ lw(a1, FieldMemOperand(v0, HeapObject::kMapOffset)); __ lbu(a1, FieldMemOperand(a1, Map::kInstanceTypeOffset)); - __ And(t4, a1, Operand(kIsNotStringMask)); + __ And(t4, v0, Operand(kIsNotStringMask)); __ Branch(&sub_string_runtime, ne, t4, Operand(zero_reg)); + // Short-cut for the case of trivial substring. + Label return_v0; + // v0: original string + // a2: result string length + __ lw(t0, FieldMemOperand(v0, String::kLengthOffset)); + __ sra(t0, t0, 1); + __ Branch(&return_v0, eq, a2, Operand(t0)); + + Label create_slice; + if (FLAG_string_slices) { + __ Branch(&create_slice, ge, a2, Operand(SlicedString::kMinLength)); + } + + // v0: original string // a1: instance type // a2: result string length // a3: from index (untagged smi) - // t1: string // t2: (a.k.a. to): to (smi) // t3: (a.k.a. from): from offset (smi) // t5: to index (untagged smi) @@ -5686,8 +5712,9 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ And(t0, a1, Operand(kStringRepresentationMask)); STATIC_ASSERT(kSeqStringTag < kConsStringTag); STATIC_ASSERT(kConsStringTag < kExternalStringTag); + STATIC_ASSERT(kConsStringTag < kSlicedStringTag); - // External strings go to runtime. + // Slices and external strings go to runtime. __ Branch(&sub_string_runtime, gt, t0, Operand(kConsStringTag)); // Sequential strings are handled directly. @@ -5696,32 +5723,32 @@ void SubStringStub::Generate(MacroAssembler* masm) { // Cons string. Try to recurse (once) on the first substring. // (This adds a little more generality than necessary to handle flattened // cons strings, but not much). - __ lw(t1, FieldMemOperand(t1, ConsString::kFirstOffset)); - __ lw(t0, FieldMemOperand(t1, HeapObject::kMapOffset)); + __ lw(v0, FieldMemOperand(v0, ConsString::kFirstOffset)); + __ lw(t0, FieldMemOperand(v0, HeapObject::kMapOffset)); __ lbu(a1, FieldMemOperand(t0, Map::kInstanceTypeOffset)); STATIC_ASSERT(kSeqStringTag == 0); - // Cons and External strings go to runtime. + // Cons, slices and external strings go to runtime. __ Branch(&sub_string_runtime, ne, a1, Operand(kStringRepresentationMask)); // Definitly a sequential string. __ bind(&seq_string); + // v0: original string // a1: instance type // a2: result string length // a3: from index (untagged smi) - // t1: string // t2: (a.k.a. to): to (smi) // t3: (a.k.a. from): from offset (smi) // t5: to index (untagged smi) - __ lw(t0, FieldMemOperand(t1, String::kLengthOffset)); + __ lw(t0, FieldMemOperand(v0, String::kLengthOffset)); __ Branch(&sub_string_runtime, lt, t0, Operand(to)); // Fail if to > length. to = no_reg; + // v0: original string or left hand side of the original cons string. // a1: instance type // a2: result string length // a3: from index (untagged smi) - // t1: string // t3: (a.k.a. from): from offset (smi) // t5: to index (untagged smi) @@ -5737,84 +5764,147 @@ void SubStringStub::Generate(MacroAssembler* masm) { // Sub string of length 2 requested. // Get the two characters forming the sub string. - __ Addu(t1, t1, Operand(a3)); - __ lbu(a3, FieldMemOperand(t1, SeqAsciiString::kHeaderSize)); - __ lbu(t0, FieldMemOperand(t1, SeqAsciiString::kHeaderSize + 1)); + __ Addu(v0, v0, Operand(a3)); + __ lbu(a3, FieldMemOperand(v0, SeqAsciiString::kHeaderSize)); + __ lbu(t0, FieldMemOperand(v0, SeqAsciiString::kHeaderSize + 1)); // Try to lookup two character string in symbol table. Label make_two_character_string; StringHelper::GenerateTwoCharacterSymbolTableProbe( masm, a3, t0, a1, t1, t2, t3, t4, &make_two_character_string); Counters* counters = masm->isolate()->counters(); - __ IncrementCounter(counters->sub_string_native(), 1, a3, t0); - __ Addu(sp, sp, Operand(3 * kPointerSize)); - __ Ret(); - + __ jmp(&return_v0); // a2: result string length. // a3: two characters combined into halfword in little endian byte order. __ bind(&make_two_character_string); __ AllocateAsciiString(v0, a2, t0, t1, t4, &sub_string_runtime); __ sh(a3, FieldMemOperand(v0, SeqAsciiString::kHeaderSize)); - __ IncrementCounter(counters->sub_string_native(), 1, a3, t0); - __ Addu(sp, sp, Operand(3 * kPointerSize)); - __ Ret(); + __ jmp(&return_v0); __ bind(&result_longer_than_two); + // Locate 'from' character of string. + __ Addu(t1, v0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); + __ sra(t4, from, 1); + __ Addu(t1, t1, t4); + // Allocate the result. __ AllocateAsciiString(v0, a2, t4, t0, a1, &sub_string_runtime); - // v0: result string. - // a2: result string length. + // v0: result string + // a2: result string length // a3: from index (untagged smi) - // t1: string. + // t1: first character of substring to copy // t3: (a.k.a. from): from offset (smi) // Locate first character of result. __ Addu(a1, v0, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - // Locate 'from' character of string. - __ Addu(t1, t1, Operand(SeqAsciiString::kHeaderSize - kHeapObjectTag)); - __ Addu(t1, t1, Operand(a3)); - // v0: result string. - // a1: first character of result string. - // a2: result string length. - // t1: first character of sub string to copy. + // v0: result string + // a1: first character of result string + // a2: result string length + // t1: first character of substring to copy STATIC_ASSERT((SeqAsciiString::kHeaderSize & kObjectAlignmentMask) == 0); StringHelper::GenerateCopyCharactersLong( masm, a1, t1, a2, a3, t0, t2, t3, t4, COPY_ASCII | DEST_ALWAYS_ALIGNED); - __ IncrementCounter(counters->sub_string_native(), 1, a3, t0); - __ Addu(sp, sp, Operand(3 * kPointerSize)); - __ Ret(); + __ jmp(&return_v0); __ bind(&non_ascii_flat); - // a2: result string length. - // t1: string. + // a2: result string length + // t1: string // t3: (a.k.a. from): from offset (smi) // Check for flat two byte string. + // Locate 'from' character of string. + __ Addu(t1, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); + // As "from" is a smi it is 2 times the value which matches the size of a two + // byte character. + STATIC_ASSERT(kSmiTagSize == 1 && kSmiTag == 0); + __ Addu(t1, t1, Operand(from)); + // Allocate the result. __ AllocateTwoByteString(v0, a2, a1, a3, t0, &sub_string_runtime); - // v0: result string. - // a2: result string length. - // t1: string. + // v0: result string + // a2: result string length + // t1: first character of substring to copy // Locate first character of result. __ Addu(a1, v0, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // Locate 'from' character of string. - __ Addu(t1, t1, Operand(SeqTwoByteString::kHeaderSize - kHeapObjectTag)); - // As "from" is a smi it is 2 times the value which matches the size of a two - // byte character. - __ Addu(t1, t1, Operand(from)); + from = no_reg; // v0: result string. // a1: first character of result. // a2: result length. - // t1: first character of string to copy. + // t1: first character of substring to copy. STATIC_ASSERT((SeqTwoByteString::kHeaderSize & kObjectAlignmentMask) == 0); StringHelper::GenerateCopyCharactersLong( masm, a1, t1, a2, a3, t0, t2, t3, t4, DEST_ALWAYS_ALIGNED); + __ jmp(&return_v0); + + if (FLAG_string_slices) { + __ bind(&create_slice); + // v0: original string + // a1: instance type + // a2: length + // a3: from index (untagged smi) + // t2 (a.k.a. to): to (smi) + // t3 (a.k.a. from): from offset (smi) + Label allocate_slice, sliced_string, seq_string; + STATIC_ASSERT(kSeqStringTag == 0); + __ And(t4, a1, Operand(kStringRepresentationMask)); + __ Branch(&seq_string, eq, t4, Operand(zero_reg)); + STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); + STATIC_ASSERT(kIsIndirectStringMask != 0); + __ And(t4, a1, Operand(kIsIndirectStringMask)); + // External string. Jump to runtime. + __ Branch(&sub_string_runtime, eq, t4, Operand(zero_reg)); + + __ And(t4, a1, Operand(kSlicedNotConsMask)); + __ Branch(&sliced_string, ne, t4, Operand(zero_reg)); + // Cons string. Check whether it is flat, then fetch first part. + __ lw(t1, FieldMemOperand(v0, ConsString::kSecondOffset)); + __ LoadRoot(t5, Heap::kEmptyStringRootIndex); + __ Branch(&sub_string_runtime, ne, t1, Operand(t5)); + __ lw(t1, FieldMemOperand(v0, ConsString::kFirstOffset)); + __ jmp(&allocate_slice); + + __ bind(&sliced_string); + // Sliced string. Fetch parent and correct start index by offset. + __ lw(t1, FieldMemOperand(v0, SlicedString::kOffsetOffset)); + __ addu(t3, t3, t1); + __ lw(t1, FieldMemOperand(v0, SlicedString::kParentOffset)); + __ jmp(&allocate_slice); + + __ bind(&seq_string); + // Sequential string. Just move string to the right register. + __ mov(t1, v0); + + __ bind(&allocate_slice); + // a1: instance type of original string + // a2: length + // t1: underlying subject string + // t3 (a.k.a. from): from offset (smi) + // Allocate new sliced string. At this point we do not reload the instance + // type including the string encoding because we simply rely on the info + // provided by the original string. It does not matter if the original + // string's encoding is wrong because we always have to recheck encoding of + // the newly created string's parent anyways due to externalized strings. + Label two_byte_slice, set_slice_header; + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ And(t4, a1, Operand(kStringEncodingMask)); + __ Branch(&two_byte_slice, eq, t4, Operand(zero_reg)); + __ AllocateAsciiSlicedString(v0, a2, a3, t0, &sub_string_runtime); + __ jmp(&set_slice_header); + __ bind(&two_byte_slice); + __ AllocateTwoByteSlicedString(v0, a2, a3, t0, &sub_string_runtime); + __ bind(&set_slice_header); + __ sw(t3, FieldMemOperand(v0, SlicedString::kOffsetOffset)); + __ sw(t1, FieldMemOperand(v0, SlicedString::kParentOffset)); + } + + __ bind(&return_v0); __ IncrementCounter(counters->sub_string_native(), 1, a3, t0); __ Addu(sp, sp, Operand(3 * kPointerSize)); __ Ret(); diff --git a/src/mips/constants-mips.cc b/src/mips/constants-mips.cc index 96a23338..d0a7af5c 100644 --- a/src/mips/constants-mips.cc +++ b/src/mips/constants-mips.cc @@ -191,6 +191,7 @@ bool Instruction::IsLinkingInstruction() const { const int op = OpcodeFieldRaw(); switch (op) { case JAL: + return true; case REGIMM: switch (RtFieldRaw()) { case BGEZAL: @@ -272,7 +273,7 @@ Instruction::Type Instruction::InstructionType() const { case MOVCI: return kRegisterType; default: - UNREACHABLE(); + return kUnsupported; }; break; case SPECIAL2: @@ -281,7 +282,7 @@ Instruction::Type Instruction::InstructionType() const { case CLZ: return kRegisterType; default: - UNREACHABLE(); + return kUnsupported; }; break; case SPECIAL3: @@ -290,7 +291,7 @@ Instruction::Type Instruction::InstructionType() const { case EXT: return kRegisterType; default: - UNREACHABLE(); + return kUnsupported; }; break; case COP1: // Coprocessor instructions. @@ -341,7 +342,7 @@ Instruction::Type Instruction::InstructionType() const { 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 6bf2570e..d76ae59f 100644 --- a/src/mips/constants-mips.h +++ b/src/mips/constants-mips.h @@ -204,6 +204,10 @@ static const int kImm26Bits = 26; 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 kFunctionFieldMask = 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 Opcode { 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 @@ inline Condition ReverseCondition(Condition cc) { // ----- 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 @@ class Instruction { 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. @@ -743,11 +771,9 @@ class Instruction { // ----------------------------------------------------------------------------- // MIPS assembly various constants. - -static const int kArgsSlotsSize = 4 * Instruction::kInstrSize; -static const int kArgsSlotsNum = 4; // C/C++ argument slots size. -static const int kCArgsSlotsSize = 4 * Instruction::kInstrSize; +static const int kCArgSlotCount = 4; +static const int kCArgsSlotsSize = kCArgSlotCount * Instruction::kInstrSize; // JS argument slots size. static const int kJSArgsSlotsSize = 0 * Instruction::kInstrSize; // Assembly builtins argument slots size. diff --git a/src/mips/disasm-mips.cc b/src/mips/disasm-mips.cc index 7df5c417..fde0c58f 100644 --- a/src/mips/disasm-mips.cc +++ b/src/mips/disasm-mips.cc @@ -112,7 +112,7 @@ class Decoder { 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 @@ void Decoder::PrintXImm16(Instruction* instr) { // 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 @@ int Decoder::FormatOption(Instruction* instr, const char* format) { } 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::DecodeTypeImmediate(Instruction* instr) { 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 @@ int Decoder::InstructionDecode(byte* instr_ptr) { break; } default: { + Format(instr, "UNSUPPORTED"); UNSUPPORTED_MIPS(); } } diff --git a/src/mips/frames-mips.h b/src/mips/frames-mips.h index 1899843a..2c838938 100644 --- a/src/mips/frames-mips.h +++ b/src/mips/frames-mips.h @@ -30,7 +30,6 @@ #ifndef V8_MIPS_FRAMES_MIPS_H_ #define V8_MIPS_FRAMES_MIPS_H_ - namespace v8 { namespace internal { @@ -40,13 +39,22 @@ namespace internal { static const int kNumRegs = 32; static const RegList kJSCallerSaved = - 1 << 2 | // v0 - 1 << 4 | // a0 - 1 << 5 | // a1 - 1 << 6 | // a2 - 1 << 7; // a3 - -static const int kNumJSCallerSaved = 5; + 1 << 2 | // v0 + 1 << 3 | // v1 + 1 << 4 | // a0 + 1 << 5 | // a1 + 1 << 6 | // a2 + 1 << 7 | // a3 + 1 << 8 | // t0 + 1 << 9 | // t1 + 1 << 10 | // t2 + 1 << 11 | // t3 + 1 << 12 | // t4 + 1 << 13 | // t5 + 1 << 14 | // t6 + 1 << 15; // t7 + +static const int kNumJSCallerSaved = 14; // Return the code of the n-th caller-saved register available to JavaScript @@ -56,19 +64,30 @@ int JSCallerSavedCode(int n); // Callee-saved registers preserved when switching from C to JavaScript. static const RegList kCalleeSaved = - // Saved temporaries. - 1 << 16 | 1 << 17 | 1 << 18 | 1 << 19 | - 1 << 20 | 1 << 21 | 1 << 22 | 1 << 23 | - // fp. - 1 << 30; + 1 << 16 | // s0 + 1 << 17 | // s1 + 1 << 18 | // s2 + 1 << 19 | // s3 + 1 << 20 | // s4 + 1 << 21 | // s5 + 1 << 22 | // s6 (roots in Javascript code) + 1 << 23 | // s7 (cp in Javascript code) + 1 << 30; // fp/s8 static const int kNumCalleeSaved = 9; +static const RegList kCalleeSavedFPU = + 1 << 20 | // f20 + 1 << 22 | // f22 + 1 << 24 | // f24 + 1 << 26 | // f26 + 1 << 28 | // f28 + 1 << 30; // f30 +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 = 16; +static const int kNumSafepointRegisters = 24; // Define the list of registers actually saved at safepoints. // Note that the number of saved registers may be smaller than the reserved @@ -82,37 +101,37 @@ typedef Object* JSCallerSavedBuffer[kNumJSCallerSaved]; static const int kUndefIndex = -1; // Map with indexes on stack that corresponds to codes of saved registers. static const int kSafepointRegisterStackIndexMap[kNumRegs] = { - kUndefIndex, - kUndefIndex, - 0, // v0 - kUndefIndex, - 1, // a0 - 2, // a1 - 3, // a2 - 4, // a3 - kUndefIndex, - kUndefIndex, - kUndefIndex, - kUndefIndex, - kUndefIndex, - kUndefIndex, - kUndefIndex, - kUndefIndex, - 5, // Saved temporaries. - 6, - 7, - 8, - 9, - 10, - 11, - 12, - kUndefIndex, - kUndefIndex, - kUndefIndex, - kUndefIndex, - 13, // gp - 14, // sp - 15, // fp + kUndefIndex, // zero_reg + kUndefIndex, // at + 0, // v0 + 1, // v1 + 2, // a0 + 3, // a1 + 4, // a2 + 5, // a3 + 6, // t0 + 7, // t1 + 8, // t2 + 9, // t3 + 10, // t4 + 11, // t5 + 12, // t6 + 13, // t7 + 14, // s0 + 15, // s1 + 16, // s2 + 17, // s3 + 18, // s4 + 19, // s5 + 20, // s6 + 21, // s7 + kUndefIndex, // t8 + kUndefIndex, // t9 + kUndefIndex, // k0 + kUndefIndex, // k1 + kUndefIndex, // gp + kUndefIndex, // sp + 22, // fp kUndefIndex }; @@ -174,9 +193,6 @@ class StandardFrameConstants : public AllStatic { static const int kRArgsSlotsSize = 4 * kPointerSize; static const int kRegularArgsSlotsSize = kRArgsSlotsSize; - // C/C++ argument slots size. - static const int kCArgSlotCount = 4; - static const int kCArgsSlotsSize = kCArgSlotCount * kPointerSize; // JS argument slots size. static const int kJSArgsSlotsSize = 0 * kPointerSize; // Assembly builtins argument slots size. diff --git a/src/mips/full-codegen-mips.cc b/src/mips/full-codegen-mips.cc index d3f89228..9a210c49 100644 --- a/src/mips/full-codegen-mips.cc +++ b/src/mips/full-codegen-mips.cc @@ -200,14 +200,14 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // Copy any necessary parameters into the context. int num_parameters = info->scope()->num_parameters(); for (int i = 0; i < num_parameters; i++) { - Slot* slot = scope()->parameter(i)->AsSlot(); - if (slot != NULL && slot->type() == Slot::CONTEXT) { + Variable* var = scope()->parameter(i); + if (var->IsContextSlot()) { int parameter_offset = StandardFrameConstants::kCallerSPOffset + (num_parameters - 1 - i) * kPointerSize; // Load parameter from stack. __ lw(a0, MemOperand(fp, parameter_offset)); // Store it in the context. - __ li(a1, Operand(Context::SlotOffset(slot->index()))); + __ li(a1, Operand(Context::SlotOffset(var->index()))); __ addu(a2, cp, a1); __ sw(a0, MemOperand(a2, 0)); // Update the write barrier. This clobbers all involved @@ -252,7 +252,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { ArgumentsAccessStub stub(type); __ CallStub(&stub); - Move(arguments->AsSlot(), v0, a1, a2); + SetVar(arguments, v0, a1, a2); } if (FLAG_trace) { @@ -266,17 +266,19 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { scope()->VisitIllegalRedeclaration(this); } else { + PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS); { Comment cmnt(masm_, "[ Declarations"); // For named function expressions, declare the function name as a // constant. if (scope()->is_function_scope() && scope()->function() != NULL) { - EmitDeclaration(scope()->function(), Variable::CONST, NULL); + int ignored = 0; + EmitDeclaration(scope()->function(), Variable::CONST, NULL, &ignored); } VisitDeclarations(scope()->declarations()); } { Comment cmnt(masm_, "[ Stack check"); - PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS); + PrepareForBailoutForId(AstNode::kDeclarationsId, NO_REGISTERS); Label ok; __ LoadRoot(t0, Heap::kStackLimitRootIndex); __ Branch(&ok, hs, sp, Operand(t0)); @@ -370,24 +372,27 @@ void FullCodeGenerator::EmitReturnSequence() { } -void FullCodeGenerator::EffectContext::Plug(Slot* slot) const { +void FullCodeGenerator::EffectContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); } -void FullCodeGenerator::AccumulatorValueContext::Plug(Slot* slot) const { - codegen()->Move(result_register(), slot); +void FullCodeGenerator::AccumulatorValueContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + codegen()->GetVar(result_register(), var); } -void FullCodeGenerator::StackValueContext::Plug(Slot* slot) const { - codegen()->Move(result_register(), slot); +void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + codegen()->GetVar(result_register(), var); __ push(result_register()); } -void FullCodeGenerator::TestContext::Plug(Slot* slot) const { +void FullCodeGenerator::TestContext::Plug(Variable* var) const { // For simplicity we always test the accumulator register. - codegen()->Move(result_register(), slot); + codegen()->GetVar(result_register(), var); codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); codegen()->DoTest(this); } @@ -620,29 +625,56 @@ void FullCodeGenerator::Split(Condition cc, } -MemOperand FullCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) { - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: - return MemOperand(fp, SlotOffset(slot)); - case Slot::CONTEXT: { - int context_chain_length = - scope()->ContextChainLength(slot->var()->scope()); - __ LoadContext(scratch, context_chain_length); - return ContextOperand(scratch, slot->index()); - } - case Slot::LOOKUP: - UNREACHABLE(); +MemOperand FullCodeGenerator::StackOperand(Variable* var) { + ASSERT(var->IsStackAllocated()); + // Offset is negative because higher indexes are at lower addresses. + int offset = -var->index() * kPointerSize; + // Adjust by a (parameter or local) base offset. + if (var->IsParameter()) { + offset += (info_->scope()->num_parameters() + 1) * kPointerSize; + } else { + offset += JavaScriptFrameConstants::kLocal0Offset; + } + return MemOperand(fp, offset); +} + + +MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) { + ASSERT(var->IsContextSlot() || var->IsStackAllocated()); + if (var->IsContextSlot()) { + int context_chain_length = scope()->ContextChainLength(var->scope()); + __ LoadContext(scratch, context_chain_length); + return ContextOperand(scratch, var->index()); + } else { + return StackOperand(var); } - UNREACHABLE(); - return MemOperand(v0, 0); } -void FullCodeGenerator::Move(Register destination, Slot* source) { +void FullCodeGenerator::GetVar(Register dest, Variable* var) { // Use destination as scratch. - MemOperand slot_operand = EmitSlotSearch(source, destination); - __ lw(destination, slot_operand); + MemOperand location = VarOperand(var, dest); + __ lw(dest, location); +} + + +void FullCodeGenerator::SetVar(Variable* var, + Register src, + Register scratch0, + Register scratch1) { + ASSERT(var->IsContextSlot() || var->IsStackAllocated()); + ASSERT(!scratch0.is(src)); + ASSERT(!scratch0.is(scratch1)); + ASSERT(!scratch1.is(src)); + MemOperand location = VarOperand(var, scratch0); + __ sw(src, location); + // Emit the write barrier code if the location is in the heap. + if (var->IsContextSlot()) { + __ RecordWrite(scratch0, + Operand(Context::SlotOffset(var->index())), + scratch1, + src); + } } @@ -672,47 +704,33 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, } -void FullCodeGenerator::Move(Slot* dst, - Register src, - Register scratch1, - Register scratch2) { - ASSERT(dst->type() != Slot::LOOKUP); // Not yet implemented. - ASSERT(!scratch1.is(src) && !scratch2.is(src)); - MemOperand location = EmitSlotSearch(dst, scratch1); - __ sw(src, location); - // Emit the write barrier code if the location is in the heap. - if (dst->type() == Slot::CONTEXT) { - __ RecordWrite(scratch1, - Operand(Context::SlotOffset(dst->index())), - scratch2, - src); - } -} - - -void FullCodeGenerator::EmitDeclaration(Variable* variable, +void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Variable::Mode mode, - FunctionLiteral* function) { - Comment cmnt(masm_, "[ Declaration"); - ASSERT(variable != NULL); // Must have been resolved. - Slot* slot = variable->AsSlot(); - ASSERT(slot != NULL); - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: - if (mode == Variable::CONST) { - __ LoadRoot(t0, Heap::kTheHoleValueRootIndex); - __ sw(t0, MemOperand(fp, SlotOffset(slot))); - } else if (function != NULL) { + FunctionLiteral* function, + int* global_count) { + // If it was not possible to allocate the variable at compile time, we + // need to "declare" it at runtime to make sure it actually exists in the + // local context. + Variable* variable = proxy->var(); + switch (variable->location()) { + case Variable::UNALLOCATED: + ++(*global_count); + break; + + case Variable::PARAMETER: + case Variable::LOCAL: + if (function != NULL) { + Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); - __ sw(result_register(), MemOperand(fp, SlotOffset(slot))); + __ sw(result_register(), StackOperand(variable)); + } else if (mode == Variable::CONST || mode == Variable::LET) { + Comment cmnt(masm_, "[ Declaration"); + __ LoadRoot(t0, Heap::kTheHoleValueRootIndex); + __ sw(t0, StackOperand(variable)); } break; - case Slot::CONTEXT: - // We bypass the general EmitSlotSearch because we know more about - // this specific context. - + case Variable::CONTEXT: // The variable in the decl always resides in the current function // context. ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); @@ -726,23 +744,28 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, __ Check(ne, "Declaration in catch context.", a1, Operand(t0)); } - if (mode == Variable::CONST) { - __ LoadRoot(at, Heap::kTheHoleValueRootIndex); - __ sw(at, ContextOperand(cp, slot->index())); - // No write barrier since the_hole_value is in old space. - } else if (function != NULL) { + if (function != NULL) { + Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); - __ sw(result_register(), ContextOperand(cp, slot->index())); - int offset = Context::SlotOffset(slot->index()); + __ sw(result_register(), ContextOperand(cp, variable->index())); + int offset = Context::SlotOffset(variable->index()); // We know that we have written a function, which is not a smi. __ mov(a1, cp); __ RecordWrite(a1, Operand(offset), a2, result_register()); + PrepareForBailoutForId(proxy->id(), NO_REGISTERS); + } else if (mode == Variable::CONST || mode == Variable::LET) { + Comment cmnt(masm_, "[ Declaration"); + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + __ sw(at, ContextOperand(cp, variable->index())); + // No write barrier since the_hole_value is in old space. + PrepareForBailoutForId(proxy->id(), NO_REGISTERS); } break; - case Slot::LOOKUP: { + case Variable::LOOKUP: { + Comment cmnt(masm_, "[ Declaration"); __ li(a2, Operand(variable->name())); - // Declaration nodes are always introduced in one of two modes. + // Declaration nodes are always introduced in one of three modes. ASSERT(mode == Variable::VAR || mode == Variable::CONST || mode == Variable::LET); @@ -752,17 +775,16 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, // Note: For variables we must not push an initial value (such as // 'undefined') because we may have a (legal) redeclaration and we // must not destroy the current value. - if (mode == Variable::CONST) { - __ LoadRoot(a0, Heap::kTheHoleValueRootIndex); - __ Push(cp, a2, a1, a0); - } else if (function != NULL) { + if (function != NULL) { __ Push(cp, a2, a1); // Push initial value for function declaration. VisitForStackValue(function); + } else if (mode == Variable::CONST || mode == Variable::LET) { + __ LoadRoot(a0, Heap::kTheHoleValueRootIndex); + __ Push(cp, a2, a1, a0); } else { ASSERT(Smi::FromInt(0) == 0); - // No initial value! - __ mov(a0, zero_reg); // Operand(Smi::FromInt(0))); + __ mov(a0, zero_reg); // Smi::FromInt(0) indicates no initial value. __ Push(cp, a2, a1, a0); } __ CallRuntime(Runtime::kDeclareContextSlot, 4); @@ -772,19 +794,16 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, } -void FullCodeGenerator::VisitDeclaration(Declaration* decl) { - EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); -} +void FullCodeGenerator::VisitDeclaration(Declaration* decl) { } void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { // Call the runtime to declare the globals. // The context is the first argument. - __ li(a2, Operand(pairs)); - __ li(a1, Operand(Smi::FromInt(is_eval() ? 1 : 0))); - __ li(a0, Operand(Smi::FromInt(strict_mode_flag()))); - __ Push(cp, a2, a1, a0); - __ CallRuntime(Runtime::kDeclareGlobals, 4); + __ li(a1, Operand(pairs)); + __ li(a0, Operand(Smi::FromInt(DeclareGlobalsFlags()))); + __ Push(cp, a1, a0); + __ CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. } @@ -1088,10 +1107,9 @@ void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { } -void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( - Slot* slot, - TypeofState typeof_state, - Label* slow) { +void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, + TypeofState typeof_state, + Label* slow) { Register current = cp; Register next = a1; Register temp = a2; @@ -1135,7 +1153,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( } __ lw(a0, GlobalObjectOperand()); - __ li(a2, Operand(slot->var()->name())); + __ li(a2, Operand(var->name())); RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) ? RelocInfo::CODE_TARGET : RelocInfo::CODE_TARGET_CONTEXT; @@ -1144,15 +1162,14 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( } -MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( - Slot* slot, - Label* slow) { - ASSERT(slot->type() == Slot::CONTEXT); +MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, + Label* slow) { + ASSERT(var->IsContextSlot()); Register context = cp; Register next = a3; Register temp = t0; - for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { + for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { if (s->num_heap_slots() > 0) { if (s->calls_eval()) { // Check that extension is NULL. @@ -1171,60 +1188,32 @@ MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( // This function is used only for loads, not stores, so it's safe to // return an cp-based operand (the write barrier cannot be allowed to // destroy the cp register). - return ContextOperand(context, slot->index()); + return ContextOperand(context, var->index()); } -void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( - Slot* slot, - TypeofState typeof_state, - Label* slow, - Label* done) { +void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var, + TypeofState typeof_state, + Label* slow, + Label* done) { // Generate fast-case code for variables that might be shadowed by // eval-introduced variables. Eval is used a lot without // introducing variables. In those cases, we do not want to // perform a runtime call for all variables in the scope // containing the eval. - if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) { - EmitLoadGlobalSlotCheckExtensions(slot, typeof_state, slow); + if (var->mode() == Variable::DYNAMIC_GLOBAL) { + EmitLoadGlobalCheckExtensions(var, typeof_state, slow); __ Branch(done); - } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { - Slot* potential_slot = slot->var()->local_if_not_shadowed()->AsSlot(); - Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite(); - if (potential_slot != NULL) { - // Generate fast case for locals that rewrite to slots. - __ lw(v0, ContextSlotOperandCheckExtensions(potential_slot, slow)); - if (potential_slot->var()->mode() == Variable::CONST) { - __ LoadRoot(at, Heap::kTheHoleValueRootIndex); - __ subu(at, v0, at); // Sub as compare: at == 0 on eq. - __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); - __ movz(v0, a0, at); // Conditional move. - } - __ Branch(done); - } else if (rewrite != NULL) { - // Generate fast case for calls of an argument function. - Property* property = rewrite->AsProperty(); - if (property != NULL) { - VariableProxy* obj_proxy = property->obj()->AsVariableProxy(); - Literal* key_literal = property->key()->AsLiteral(); - if (obj_proxy != NULL && - key_literal != NULL && - obj_proxy->IsArguments() && - key_literal->handle()->IsSmi()) { - // Load arguments object if there are no eval-introduced - // variables. Then load the argument from the arguments - // object using keyed load. - __ lw(a1, - ContextSlotOperandCheckExtensions(obj_proxy->var()->AsSlot(), - slow)); - __ li(a0, Operand(key_literal->handle())); - Handle<Code> ic = - isolate()->builtins()->KeyedLoadIC_Initialize(); - __ Call(ic, RelocInfo::CODE_TARGET, GetPropertyId(property)); - __ Branch(done); - } - } + } else if (var->mode() == Variable::DYNAMIC_LOCAL) { + Variable* local = var->local_if_not_shadowed(); + __ lw(v0, ContextSlotOperandCheckExtensions(local, slow)); + if (local->mode() == Variable::CONST) { + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + __ subu(at, v0, at); // Sub as compare: at == 0 on eq. + __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); + __ movz(v0, a0, at); // Conditional move: return Undefined if TheHole. } + __ Branch(done); } } @@ -1234,54 +1223,63 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { SetSourcePosition(proxy->position()); Variable* var = proxy->var(); - // Three cases: non-this global variables, lookup slots, and all other - // types of slots. - Slot* slot = var->AsSlot(); - ASSERT((var->is_global() && !var->is_this()) == (slot == NULL)); - - if (slot == NULL) { - Comment cmnt(masm_, "Global variable"); - // Use inline caching. Variable name is passed in a2 and the global - // object (receiver) in a0. - __ lw(a0, GlobalObjectOperand()); - __ li(a2, Operand(var->name())); - Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); - __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); - context()->Plug(v0); - - } else if (slot->type() == Slot::LOOKUP) { - Label done, slow; - - // Generate code for loading from variables potentially shadowed - // by eval-introduced variables. - EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done); - - __ bind(&slow); - Comment cmnt(masm_, "Lookup slot"); - __ li(a1, Operand(var->name())); - __ Push(cp, a1); // Context and name. - __ CallRuntime(Runtime::kLoadContextSlot, 2); - __ bind(&done); + // Three cases: global variables, lookup variables, and all other types of + // variables. + switch (var->location()) { + case Variable::UNALLOCATED: { + Comment cmnt(masm_, "Global variable"); + // Use inline caching. Variable name is passed in a2 and the global + // object (receiver) in a0. + __ lw(a0, GlobalObjectOperand()); + __ li(a2, Operand(var->name())); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); + __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); + context()->Plug(v0); + break; + } - context()->Plug(v0); + case Variable::PARAMETER: + case Variable::LOCAL: + case Variable::CONTEXT: { + Comment cmnt(masm_, var->IsContextSlot() + ? "Context variable" + : "Stack variable"); + if (var->mode() != Variable::LET && var->mode() != Variable::CONST) { + context()->Plug(var); + } else { + // Let and const need a read barrier. + GetVar(v0, var); + __ LoadRoot(at, Heap::kTheHoleValueRootIndex); + __ subu(at, v0, at); // Sub as compare: at == 0 on eq. + if (var->mode() == Variable::LET) { + Label done; + __ Branch(&done, ne, at, Operand(zero_reg)); + __ li(a0, Operand(var->name())); + __ push(a0); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ bind(&done); + } else { + __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); + __ movz(v0, a0, at); // Conditional move: Undefined if TheHole. + } + context()->Plug(v0); + } + break; + } - } else { - Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) - ? "Context slot" - : "Stack slot"); - if (var->mode() == Variable::CONST) { - // Constants may be the hole value if they have not been initialized. - // Unhole them. - MemOperand slot_operand = EmitSlotSearch(slot, a0); - __ lw(v0, slot_operand); - __ LoadRoot(at, Heap::kTheHoleValueRootIndex); - __ subu(at, v0, at); // Sub as compare: at == 0 on eq. - __ LoadRoot(a0, Heap::kUndefinedValueRootIndex); - __ movz(v0, a0, at); // Conditional move. - context()->Plug(v0); - } else { - context()->Plug(slot); - } + case Variable::LOOKUP: { + Label done, slow; + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done); + __ bind(&slow); + Comment cmnt(masm_, "Lookup variable"); + __ li(a1, Operand(var->name())); + __ Push(cp, a1); // Context and name. + __ CallRuntime(Runtime::kLoadContextSlot, 2); + __ bind(&done); + context()->Plug(v0); + } } } @@ -1819,14 +1817,8 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) { - ASSERT(var != NULL); - ASSERT(var->is_global() || var->AsSlot() != NULL); - - if (var->is_global()) { - ASSERT(!var->is_this()); - // Assignment to a global variable. Use inline caching for the - // assignment. Right-hand-side value is passed in a0, variable name in - // a2, and the global object in a1. + if (var->IsUnallocated()) { + // Global var, const, or let. __ mov(a0, result_register()); __ li(a2, Operand(var->name())); __ lw(a1, GlobalObjectOperand()); @@ -1836,66 +1828,83 @@ void FullCodeGenerator::EmitVariableAssignment(Variable* var, __ Call(ic, RelocInfo::CODE_TARGET_CONTEXT); } else if (op == Token::INIT_CONST) { - // Like var declarations, const declarations are hoisted to function - // scope. However, unlike var initializers, const initializers are able - // to drill a hole to that function context, even from inside a 'with' - // context. We thus bypass the normal static scope lookup. - Slot* slot = var->AsSlot(); - Label skip; - switch (slot->type()) { - case Slot::PARAMETER: - // No const parameters. - UNREACHABLE(); - break; - case Slot::LOCAL: - // Detect const reinitialization by checking for the hole value. - __ lw(a1, MemOperand(fp, SlotOffset(slot))); - __ LoadRoot(t0, Heap::kTheHoleValueRootIndex); - __ Branch(&skip, ne, a1, Operand(t0)); - __ sw(result_register(), MemOperand(fp, SlotOffset(slot))); - break; - case Slot::CONTEXT: - case Slot::LOOKUP: - __ push(result_register()); - __ li(a0, Operand(slot->var()->name())); - __ Push(cp, a0); // Context and name. - __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); - break; + // Const initializers need a write barrier. + ASSERT(!var->IsParameter()); // No const parameters. + if (var->IsStackLocal()) { + Label skip; + __ lw(a1, StackOperand(var)); + __ LoadRoot(t0, Heap::kTheHoleValueRootIndex); + __ Branch(&skip, ne, a1, Operand(t0)); + __ sw(result_register(), StackOperand(var)); + __ bind(&skip); + } else { + ASSERT(var->IsContextSlot() || var->IsLookupSlot()); + // Like var declarations, const declarations are hoisted to function + // scope. However, unlike var initializers, const initializers are + // able to drill a hole to that function context, even from inside a + // 'with' context. We thus bypass the normal static scope lookup for + // var->IsContextSlot(). + __ push(v0); + __ li(a0, Operand(var->name())); + __ Push(cp, a0); // Context and name. + __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); } - __ bind(&skip); - - } else if (var->mode() != Variable::CONST) { - // Perform the assignment for non-const variables. Const assignments - // are simply skipped. - Slot* slot = var->AsSlot(); - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: - // Perform the assignment. - __ sw(result_register(), MemOperand(fp, SlotOffset(slot))); - break; - case Slot::CONTEXT: { - MemOperand target = EmitSlotSearch(slot, a1); - // Perform the assignment and issue the write barrier. - __ sw(result_register(), target); + } else if (var->mode() == Variable::LET && op != Token::INIT_LET) { + // Non-initializing assignment to let variable needs a write barrier. + if (var->IsLookupSlot()) { + __ push(v0); // Value. + __ li(a1, Operand(var->name())); + __ li(a0, Operand(Smi::FromInt(strict_mode_flag()))); + __ Push(cp, a1, a0); // Context, name, strict mode. + __ CallRuntime(Runtime::kStoreContextSlot, 4); + } else { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + Label assign; + MemOperand location = VarOperand(var, a1); + __ lw(a3, location); + __ LoadRoot(t0, Heap::kTheHoleValueRootIndex); + __ Branch(&assign, ne, a3, Operand(t0)); + __ li(a3, Operand(var->name())); + __ push(a3); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + // Perform the assignment. + __ bind(&assign); + __ sw(result_register(), location); + if (var->IsContextSlot()) { // RecordWrite may destroy all its register arguments. __ mov(a3, result_register()); - int offset = FixedArray::kHeaderSize + slot->index() * kPointerSize; + int offset = Context::SlotOffset(var->index()); __ RecordWrite(a1, Operand(offset), a2, a3); - break; } + } - case Slot::LOOKUP: - // Call the runtime for the assignment. - __ push(v0); // Value. - __ li(a1, Operand(slot->var()->name())); - __ li(a0, Operand(Smi::FromInt(strict_mode_flag()))); - __ Push(cp, a1, a0); // Context, name, strict mode. - __ CallRuntime(Runtime::kStoreContextSlot, 4); - break; + } else if (var->mode() != Variable::CONST) { + // Assignment to var or initializing assignment to let. + if (var->IsStackAllocated() || var->IsContextSlot()) { + MemOperand location = VarOperand(var, a1); + if (FLAG_debug_code && op == Token::INIT_LET) { + // Check for an uninitialized let binding. + __ lw(a2, location); + __ LoadRoot(t0, Heap::kTheHoleValueRootIndex); + __ Check(eq, "Let binding re-initialization.", a2, Operand(t0)); + } + // Perform the assignment. + __ sw(v0, location); + if (var->IsContextSlot()) { + __ mov(a3, v0); + __ RecordWrite(a1, Operand(Context::SlotOffset(var->index())), a2, a3); + } + } else { + ASSERT(var->IsLookupSlot()); + __ push(v0); // Value. + __ li(a1, Operand(var->name())); + __ li(a0, Operand(Smi::FromInt(strict_mode_flag()))); + __ Push(cp, a1, a0); // Context, name, strict mode. + __ CallRuntime(Runtime::kStoreContextSlot, 4); } } + // Non-initializing assignments to consts are ignored. } @@ -2033,9 +2042,8 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, // 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. @@ -2066,9 +2074,8 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, // 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); @@ -2089,8 +2096,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { } // 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. @@ -2113,8 +2119,13 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, int receiver_offset = 2 + info_->scope()->num_parameters(); __ lw(a1, MemOperand(fp, receiver_offset * kPointerSize)); __ push(a1); - // Push the strict mode flag. - __ li(a1, Operand(Smi::FromInt(strict_mode_flag()))); + // Push the strict mode flag. In harmony mode every eval call + // is a strict mode eval call. + StrictModeFlag strict_mode = strict_mode_flag(); + if (FLAG_harmony_block_scoping) { + strict_mode = kStrictMode; + } + __ li(a1, Operand(Smi::FromInt(strict_mode))); __ push(a1); __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP @@ -2131,10 +2142,11 @@ void FullCodeGenerator::VisitCall(Call* expr) { #endif Comment cmnt(masm_, "[ Call"); - Expression* fun = expr->expression(); - Variable* var = fun->AsVariableProxy()->AsVariable(); + Expression* callee = expr->expression(); + VariableProxy* proxy = callee->AsVariableProxy(); + Property* property = callee->AsProperty(); - if (var != NULL && var->is_possibly_eval()) { + if (proxy != NULL && proxy->var()->is_possibly_eval()) { // In a call to eval, we first call %ResolvePossiblyDirectEval to // resolve the function we need to call and the receiver of the // call. Then we call the resolved function using the given @@ -2143,7 +2155,7 @@ void FullCodeGenerator::VisitCall(Call* expr) { int arg_count = args->length(); { PreservePositionScope pos_scope(masm()->positions_recorder()); - VisitForStackValue(fun); + VisitForStackValue(callee); __ LoadRoot(a2, Heap::kUndefinedValueRootIndex); __ push(a2); // Reserved receiver slot. @@ -2151,16 +2163,16 @@ void FullCodeGenerator::VisitCall(Call* expr) { for (int i = 0; i < arg_count; i++) { VisitForStackValue(args->at(i)); } + // If we know that eval can only be shadowed by eval-introduced // variables we attempt to load the global eval function directly // in generated code. If we succeed, there is no need to perform a // context lookup in the runtime system. Label done; - if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) { + Variable* var = proxy->var(); + if (!var->IsUnallocated() && var->mode() == Variable::DYNAMIC_GLOBAL) { Label slow; - EmitLoadGlobalSlotCheckExtensions(var->AsSlot(), - NOT_INSIDE_TYPEOF, - &slow); + EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow); // Push the function and resolve eval. __ push(v0); EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); @@ -2168,14 +2180,12 @@ void FullCodeGenerator::VisitCall(Call* expr) { __ bind(&slow); } - // Push copy of the function (found below the arguments) and + // Push a copy of the function (found below the arguments) and // resolve eval. __ lw(a1, MemOperand(sp, (arg_count + 1) * kPointerSize)); __ push(a1); EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); - if (done.is_linked()) { - __ bind(&done); - } + __ bind(&done); // The runtime call returns a pair of values in v0 (function) and // v1 (receiver). Touch up the stack with the right values. @@ -2184,37 +2194,32 @@ void FullCodeGenerator::VisitCall(Call* expr) { } // 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. __ lw(cp, MemOperand(fp, StandardFrameConstants::kContextOffset)); context()->DropAndPlug(1, v0); - } else if (var != NULL && !var->is_this() && var->is_global()) { + } else if (proxy != NULL && proxy->var()->IsUnallocated()) { // Push global object as receiver for the call IC. __ lw(a0, GlobalObjectOperand()); __ push(a0); - EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); - } else if (var != NULL && var->AsSlot() != NULL && - var->AsSlot()->type() == Slot::LOOKUP) { + EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT); + } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { // Call to a lookup slot (dynamically introduced variable). Label slow, done; { PreservePositionScope scope(masm()->positions_recorder()); // Generate code for loading from variables potentially shadowed // by eval-introduced variables. - EmitDynamicLoadFromSlotFastCase(var->AsSlot(), - NOT_INSIDE_TYPEOF, - &slow, - &done); + EmitDynamicLookupFastCase(proxy->var(), NOT_INSIDE_TYPEOF, &slow, &done); } __ bind(&slow); // Call the runtime to find the function to call (returned in v0) // and the object holding it (returned in v1). __ push(context_register()); - __ li(a2, Operand(var->name())); + __ li(a2, Operand(proxy->name())); __ push(a2); __ CallRuntime(Runtime::kLoadContextSlot, 2); __ Push(v0, v1); // Function, receiver. @@ -2239,26 +2244,21 @@ void FullCodeGenerator::VisitCall(Call* expr) { // by LoadContextSlot. That object could be the hole if the // receiver is implicitly the global object. EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT); - } else if (fun->AsProperty() != NULL) { - // Call to an object property. - Property* prop = fun->AsProperty(); - Literal* key = prop->key()->AsLiteral(); - if (key != NULL && key->handle()->IsSymbol()) { - // Call to a named property, use call IC. - { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(prop->obj()); - } - EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); + } else if (property != NULL) { + { PreservePositionScope scope(masm()->positions_recorder()); + VisitForStackValue(property->obj()); + } + if (property->key()->IsPropertyName()) { + EmitCallWithIC(expr, + property->key()->AsLiteral()->handle(), + RelocInfo::CODE_TARGET); } else { - // Call to a keyed property. - { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(prop->obj()); - } - EmitKeyedCallWithIC(expr, prop->key()); + EmitKeyedCallWithIC(expr, property->key()); } } else { + // Call to an arbitrary expression not handled specially above. { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(fun); + VisitForStackValue(callee); } // Load global receiver object. __ lw(a1, GlobalObjectOperand()); @@ -3206,7 +3206,7 @@ void FullCodeGenerator::EmitGetFromCache(ZoneList<Expression*>* args) { Label done, not_found; - ASSERT(kSmiTag == 0 && kSmiTagSize == 1); + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize == 1); __ lw(a2, FieldMemOperand(cache, JSFunctionResultCache::kFingerOffset)); // a2 now holds finger offset as a smi. __ Addu(a3, cache, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); @@ -3570,9 +3570,7 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { __ 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)); @@ -3588,32 +3586,32 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { switch (expr->op()) { case Token::DELETE: { Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); - Property* prop = expr->expression()->AsProperty(); - Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); + Property* property = expr->expression()->AsProperty(); + VariableProxy* proxy = expr->expression()->AsVariableProxy(); - if (prop != NULL) { - VisitForStackValue(prop->obj()); - VisitForStackValue(prop->key()); + if (property != NULL) { + VisitForStackValue(property->obj()); + VisitForStackValue(property->key()); __ li(a1, Operand(Smi::FromInt(strict_mode_flag()))); __ push(a1); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); context()->Plug(v0); - } else if (var != NULL) { + } else if (proxy != NULL) { + Variable* var = proxy->var(); // Delete of an unqualified identifier is disallowed in strict mode - // but "delete this" is. + // but "delete this" is allowed. ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this()); - if (var->is_global()) { + if (var->IsUnallocated()) { __ lw(a2, GlobalObjectOperand()); __ li(a1, Operand(var->name())); __ li(a0, Operand(Smi::FromInt(kNonStrictMode))); __ Push(a2, a1, a0); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); context()->Plug(v0); - } else if (var->AsSlot() != NULL && - var->AsSlot()->type() != Slot::LOOKUP) { + } else if (var->IsStackAllocated() || var->IsContextSlot()) { // Result of deleting non-global, non-dynamic variables is false. // The subexpression does not have side effects. - context()->Plug(false); + context()->Plug(var->is_this()); } else { // Non-global variable. Call the runtime to try to delete from the // context where the variable was introduced. @@ -3888,8 +3886,10 @@ void FullCodeGenerator::VisitCountOperation(CountOperation* expr) { void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { + ASSERT(!context()->IsEffect()); + ASSERT(!context()->IsTest()); VariableProxy* proxy = expr->AsVariableProxy(); - if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { + if (proxy != NULL && proxy->var()->IsUnallocated()) { Comment cmnt(masm_, "Global variable"); __ lw(a0, GlobalObjectOperand()); __ li(a2, Operand(proxy->name())); @@ -3899,15 +3899,12 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { __ Call(ic); PrepareForBailout(expr, TOS_REG); context()->Plug(v0); - } else if (proxy != NULL && - proxy->var()->AsSlot() != NULL && - proxy->var()->AsSlot()->type() == Slot::LOOKUP) { + } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { Label done, slow; // Generate code for loading from variables potentially shadowed // by eval-introduced variables. - Slot* slot = proxy->var()->AsSlot(); - EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done); + EmitDynamicLookupFastCase(proxy->var(), INSIDE_TYPEOF, &slow, &done); __ bind(&slow); __ li(a0, Operand(proxy->name())); @@ -4198,7 +4195,7 @@ void FullCodeGenerator::EnterFinallyBlock() { // Cook return address in link register to stack (smi encoded Code* delta). __ Subu(a1, ra, Operand(masm_->CodeObject())); ASSERT_EQ(1, kSmiTagSize + kSmiShiftSize); - ASSERT_EQ(0, kSmiTag); + STATIC_ASSERT(0 == kSmiTag); __ Addu(a1, a1, Operand(a1)); // Convert to smi. __ push(a1); } diff --git a/src/mips/ic-mips.cc b/src/mips/ic-mips.cc index 85cb9164..a76c215a 100644 --- a/src/mips/ic-mips.cc +++ b/src/mips/ic-mips.cc @@ -146,7 +146,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, __ lw(scratch1, FieldMemOperand(scratch2, kDetailsOffset)); __ And(at, scratch1, - Operand(PropertyDetails::TypeField::mask() << kSmiTagSize)); + Operand(PropertyDetails::TypeField::kMask << kSmiTagSize)); __ Branch(miss, ne, at, Operand(zero_reg)); // Get the value at the masked, scaled index and return. @@ -196,9 +196,9 @@ static void GenerateDictionaryStore(MacroAssembler* masm, const int kElementsStartOffset = StringDictionary::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize; const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; - const int kTypeAndReadOnlyMask - = (PropertyDetails::TypeField::mask() | - PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize; + const int kTypeAndReadOnlyMask = + (PropertyDetails::TypeField::kMask | + PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize; __ lw(scratch1, FieldMemOperand(scratch2, kDetailsOffset)); __ And(at, scratch1, Operand(kTypeAndReadOnlyMask)); __ Branch(miss, ne, at, Operand(zero_reg)); @@ -338,7 +338,7 @@ static void GenerateFastArrayLoad(MacroAssembler* masm, __ Addu(scratch1, elements, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); // The key is a smi. - ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); __ sll(at, key, kPointerSizeLog2 - kSmiTagSize); __ addu(at, at, scratch1); __ lw(scratch2, MemOperand(at)); @@ -372,7 +372,7 @@ static void GenerateKeyStringCheck(MacroAssembler* masm, // Is the string a symbol? // map: key map __ lbu(hash, FieldMemOperand(map, Map::kInstanceTypeOffset)); - ASSERT(kSymbolTag != 0); + STATIC_ASSERT(kSymbolTag != 0); __ And(at, hash, Operand(kIsSymbolMask)); __ Branch(not_symbol, eq, at, Operand(zero_reg)); } @@ -395,7 +395,6 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Probe the stub cache. Code::Flags flags = Code::ComputeFlags(kind, - NOT_IN_LOOP, MONOMORPHIC, extra_ic_state, NORMAL, @@ -732,9 +731,7 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { // ----------------------------------- // 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); @@ -1269,7 +1266,7 @@ void KeyedStoreIC::GenerateGeneric(MacroAssembler* masm, __ lw(t0, FieldMemOperand(elements, FixedArray::kLengthOffset)); __ Branch(&slow, hs, key, Operand(t0)); // Calculate key + 1 as smi. - ASSERT_EQ(0, kSmiTag); + STATIC_ASSERT(0 == kSmiTag); __ Addu(t3, key, Operand(Smi::FromInt(1))); __ sw(t3, FieldMemOperand(receiver, JSArray::kLengthOffset)); __ Branch(&fast); @@ -1395,10 +1392,8 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm, // ----------------------------------- // 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/macro-assembler-mips.cc b/src/mips/macro-assembler-mips.cc index c7f727be..4c48ef18 100644 --- a/src/mips/macro-assembler-mips.cc +++ b/src/mips/macro-assembler-mips.cc @@ -441,7 +441,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, const int kDetailsOffset = NumberDictionary::kElementsStartOffset + 2 * kPointerSize; lw(reg1, FieldMemOperand(reg2, kDetailsOffset)); - And(at, reg1, Operand(Smi::FromInt(PropertyDetails::TypeField::mask()))); + And(at, reg1, Operand(Smi::FromInt(PropertyDetails::TypeField::kMask))); Branch(miss, ne, at, Operand(zero_reg)); // Get the value at the masked, scaled index and return. @@ -703,52 +703,114 @@ void MacroAssembler::li(Register rd, Operand j, bool gen2instr) { void MacroAssembler::MultiPush(RegList regs) { - int16_t NumSaved = 0; - int16_t NumToPush = NumberOfBitsSet(regs); + int16_t num_to_push = NumberOfBitsSet(regs); + int16_t stack_offset = num_to_push * kPointerSize; - addiu(sp, sp, -4 * NumToPush); + Subu(sp, sp, Operand(stack_offset)); for (int16_t i = kNumRegisters; i > 0; i--) { if ((regs & (1 << i)) != 0) { - sw(ToRegister(i), MemOperand(sp, 4 * (NumToPush - ++NumSaved))); + stack_offset -= kPointerSize; + sw(ToRegister(i), MemOperand(sp, stack_offset)); } } } void MacroAssembler::MultiPushReversed(RegList regs) { - int16_t NumSaved = 0; - int16_t NumToPush = NumberOfBitsSet(regs); + int16_t num_to_push = NumberOfBitsSet(regs); + int16_t stack_offset = num_to_push * kPointerSize; - addiu(sp, sp, -4 * NumToPush); + Subu(sp, sp, Operand(stack_offset)); for (int16_t i = 0; i < kNumRegisters; i++) { if ((regs & (1 << i)) != 0) { - sw(ToRegister(i), MemOperand(sp, 4 * (NumToPush - ++NumSaved))); + stack_offset -= kPointerSize; + sw(ToRegister(i), MemOperand(sp, stack_offset)); } } } void MacroAssembler::MultiPop(RegList regs) { - int16_t NumSaved = 0; + int16_t stack_offset = 0; for (int16_t i = 0; i < kNumRegisters; i++) { if ((regs & (1 << i)) != 0) { - lw(ToRegister(i), MemOperand(sp, 4 * (NumSaved++))); + lw(ToRegister(i), MemOperand(sp, stack_offset)); + stack_offset += kPointerSize; } } - addiu(sp, sp, 4 * NumSaved); + addiu(sp, sp, stack_offset); } void MacroAssembler::MultiPopReversed(RegList regs) { - int16_t NumSaved = 0; + int16_t stack_offset = 0; for (int16_t i = kNumRegisters; i > 0; i--) { if ((regs & (1 << i)) != 0) { - lw(ToRegister(i), MemOperand(sp, 4 * (NumSaved++))); + lw(ToRegister(i), MemOperand(sp, stack_offset)); + stack_offset += kPointerSize; } } - addiu(sp, sp, 4 * NumSaved); + addiu(sp, sp, stack_offset); +} + + +void MacroAssembler::MultiPushFPU(RegList regs) { + CpuFeatures::Scope scope(FPU); + int16_t num_to_push = NumberOfBitsSet(regs); + int16_t stack_offset = num_to_push * kDoubleSize; + + Subu(sp, sp, Operand(stack_offset)); + for (int16_t i = kNumRegisters; i > 0; i--) { + if ((regs & (1 << i)) != 0) { + stack_offset -= kDoubleSize; + sdc1(FPURegister::from_code(i), MemOperand(sp, stack_offset)); + } + } +} + + +void MacroAssembler::MultiPushReversedFPU(RegList regs) { + CpuFeatures::Scope scope(FPU); + int16_t num_to_push = NumberOfBitsSet(regs); + int16_t stack_offset = num_to_push * kDoubleSize; + + Subu(sp, sp, Operand(stack_offset)); + for (int16_t i = 0; i < kNumRegisters; i++) { + if ((regs & (1 << i)) != 0) { + stack_offset -= kDoubleSize; + sdc1(FPURegister::from_code(i), MemOperand(sp, stack_offset)); + } + } +} + + +void MacroAssembler::MultiPopFPU(RegList regs) { + CpuFeatures::Scope scope(FPU); + int16_t stack_offset = 0; + + for (int16_t i = 0; i < kNumRegisters; i++) { + if ((regs & (1 << i)) != 0) { + ldc1(FPURegister::from_code(i), MemOperand(sp, stack_offset)); + stack_offset += kDoubleSize; + } + } + addiu(sp, sp, stack_offset); +} + + +void MacroAssembler::MultiPopReversedFPU(RegList regs) { + CpuFeatures::Scope scope(FPU); + int16_t stack_offset = 0; + + for (int16_t i = kNumRegisters; i > 0; i--) { + if ((regs & (1 << i)) != 0) { + ldc1(FPURegister::from_code(i), MemOperand(sp, stack_offset)); + stack_offset += kDoubleSize; + } + } + addiu(sp, sp, stack_offset); } @@ -2275,7 +2337,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, li(t0, Operand(StackHandler::TRY_FINALLY)); } // Save the current handler as the next handler. - li(t2, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); + li(t2, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); lw(t1, MemOperand(t2)); addiu(sp, sp, -StackHandlerConstants::kSize); @@ -2297,7 +2359,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, li(t0, Operand(StackHandler::ENTRY)); // Save the current handler as the next handler. - li(t2, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); + li(t2, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); lw(t1, MemOperand(t2)); ASSERT(Smi::FromInt(0) == 0); // Used for no context. @@ -2319,7 +2381,7 @@ void MacroAssembler::PopTryHandler() { STATIC_ASSERT(StackHandlerConstants::kNextOffset == 0); pop(a1); Addu(sp, sp, Operand(StackHandlerConstants::kSize - kPointerSize)); - li(at, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); + li(at, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); sw(a1, MemOperand(at)); } @@ -2337,7 +2399,7 @@ void MacroAssembler::Throw(Register value) { STATIC_ASSERT(StackHandlerConstants::kPCOffset == 4 * kPointerSize); // Drop the sp to the top of the handler. - li(a3, Operand(ExternalReference(Isolate::k_handler_address, + li(a3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); lw(sp, MemOperand(a3)); @@ -2400,7 +2462,7 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, Move(v0, value); // Drop sp to the top stack handler. - li(a3, Operand(ExternalReference(Isolate::k_handler_address, isolate()))); + li(a3, Operand(ExternalReference(Isolate::kHandlerAddress, isolate()))); lw(sp, MemOperand(a3)); // Unwind the handlers until the ENTRY handler is found. @@ -2423,7 +2485,7 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, if (type == OUT_OF_MEMORY) { // Set external caught exception to false. ExternalReference external_caught( - Isolate::k_external_caught_exception_address, isolate()); + Isolate::kExternalCaughtExceptionAddress, isolate()); li(a0, Operand(false, RelocInfo::NONE)); li(a2, Operand(external_caught)); sw(a0, MemOperand(a2)); @@ -2431,7 +2493,7 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, // Set pending exception and v0 to out of memory exception. Failure* out_of_memory = Failure::OutOfMemoryException(); li(v0, Operand(reinterpret_cast<int32_t>(out_of_memory))); - li(a2, Operand(ExternalReference(Isolate::k_pending_exception_address, + li(a2, Operand(ExternalReference(Isolate::kPendingExceptionAddress, isolate()))); sw(v0, MemOperand(a2)); } @@ -2753,6 +2815,46 @@ void MacroAssembler::AllocateAsciiConsString(Register result, } +void MacroAssembler::AllocateTwoByteSlicedString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required) { + AllocateInNewSpace(SlicedString::kSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + InitializeNewString(result, + length, + Heap::kSlicedStringMapRootIndex, + scratch1, + scratch2); +} + + +void MacroAssembler::AllocateAsciiSlicedString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required) { + AllocateInNewSpace(SlicedString::kSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + InitializeNewString(result, + length, + Heap::kSlicedAsciiStringMapRootIndex, + scratch1, + scratch2); +} + + // Allocates a heap number or jumps to the label if the young space is full and // a scavenge is needed. void MacroAssembler::AllocateHeapNumber(Register result, @@ -2873,7 +2975,7 @@ void MacroAssembler::CopyBytes(Register src, void MacroAssembler::CheckFastElements(Register map, Register scratch, Label* fail) { - STATIC_ASSERT(JSObject::FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 0); lbu(scratch, FieldMemOperand(map, Map::kBitField2Offset)); Branch(fail, hi, scratch, Operand(Map::kMaximumBitField2FastElementValue)); } @@ -3871,9 +3973,9 @@ void MacroAssembler::EnterExitFrame(bool save_doubles, sw(t8, MemOperand(fp, ExitFrameConstants::kCodeOffset)); // Save the frame pointer and the context in top. - li(t8, Operand(ExternalReference(Isolate::k_c_entry_fp_address, isolate()))); + li(t8, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate()))); sw(fp, MemOperand(t8)); - li(t8, Operand(ExternalReference(Isolate::k_context_address, isolate()))); + li(t8, Operand(ExternalReference(Isolate::kContextAddress, isolate()))); sw(cp, MemOperand(t8)); const int frame_alignment = MacroAssembler::ActivationFrameAlignment(); @@ -3923,11 +4025,11 @@ void MacroAssembler::LeaveExitFrame(bool save_doubles, } // Clear top frame. - li(t8, Operand(ExternalReference(Isolate::k_c_entry_fp_address, isolate()))); + li(t8, Operand(ExternalReference(Isolate::kCEntryFPAddress, isolate()))); sw(zero_reg, MemOperand(t8)); // Restore current context from top and clear it in debug mode. - li(t8, Operand(ExternalReference(Isolate::k_context_address, isolate()))); + li(t8, Operand(ExternalReference(Isolate::kContextAddress, isolate()))); lw(cp, MemOperand(t8)); #ifdef DEBUG sw(a3, MemOperand(t8)); @@ -4151,11 +4253,9 @@ void MacroAssembler::PrepareCallCFunction(int num_arguments, Register scratch) { // mips, even though those argument slots are not normally used. // Remaining arguments are pushed on the stack, above (higher address than) // the argument slots. - ASSERT(StandardFrameConstants::kCArgsSlotsSize % kPointerSize == 0); int stack_passed_arguments = ((num_arguments <= kRegisterPassedArguments) ? 0 : num_arguments - kRegisterPassedArguments) + - (StandardFrameConstants::kCArgsSlotsSize / - kPointerSize); + kCArgSlotCount; if (frame_alignment > kPointerSize) { // Make stack end at alignment and make room for num_arguments - 4 words // and the original value of sp. @@ -4227,11 +4327,9 @@ void MacroAssembler::CallCFunctionHelper(Register function, Call(function); - ASSERT(StandardFrameConstants::kCArgsSlotsSize % kPointerSize == 0); int stack_passed_arguments = ((num_arguments <= kRegisterPassedArguments) ? 0 : num_arguments - kRegisterPassedArguments) + - (StandardFrameConstants::kCArgsSlotsSize / - kPointerSize); + kCArgSlotCount; if (OS::ActivationFrameAlignment() > kPointerSize) { lw(sp, MemOperand(sp, stack_passed_arguments * kPointerSize)); diff --git a/src/mips/macro-assembler-mips.h b/src/mips/macro-assembler-mips.h index 0fcf6f1d..5dd012e9 100644 --- a/src/mips/macro-assembler-mips.h +++ b/src/mips/macro-assembler-mips.h @@ -362,6 +362,16 @@ class MacroAssembler: public Assembler { Register scratch1, Register scratch2, Label* gc_required); + void AllocateTwoByteSlicedString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required); + void AllocateAsciiSlicedString(Register result, + Register length, + Register scratch1, + Register scratch2, + Label* gc_required); // Allocates a heap number or jumps to the gc_required label if the young // space is full and a scavenge is needed. All registers are clobbered also @@ -442,6 +452,9 @@ class MacroAssembler: public Assembler { void MultiPush(RegList regs); void MultiPushReversed(RegList regs); + void MultiPushFPU(RegList regs); + void MultiPushReversedFPU(RegList regs); + // Lower case push() for compatibility with arch-independent code. void push(Register src) { Addu(sp, sp, Operand(-kPointerSize)); @@ -487,6 +500,9 @@ class MacroAssembler: public Assembler { void MultiPop(RegList regs); void MultiPopReversed(RegList regs); + void MultiPopFPU(RegList regs); + void MultiPopReversedFPU(RegList regs); + // Lower case pop() for compatibility with arch-independent code. void pop(Register dst) { lw(dst, MemOperand(sp, 0)); @@ -1197,10 +1213,9 @@ static inline MemOperand FieldMemOperand(Register object, int offset) { // Generate a MemOperand for storing arguments 5..N on the stack // when calling CallCFunction(). static inline MemOperand CFunctionArgumentOperand(int index) { - ASSERT(index > StandardFrameConstants::kCArgSlotCount); + ASSERT(index > kCArgSlotCount); // Argument 5 takes the slot just past the four Arg-slots. - int offset = - (index - 5) * kPointerSize + StandardFrameConstants::kCArgsSlotsSize; + int offset = (index - 5) * kPointerSize + kCArgsSlotsSize; return MemOperand(sp, offset); } diff --git a/src/mips/regexp-macro-assembler-mips.h b/src/mips/regexp-macro-assembler-mips.h index 7fe0c886..d42d4cf6 100644 --- a/src/mips/regexp-macro-assembler-mips.h +++ b/src/mips/regexp-macro-assembler-mips.h @@ -118,6 +118,7 @@ class RegExpMacroAssemblerMIPS: public NativeRegExpMacroAssembler { static int CheckStackGuardState(Address* return_address, Code* re_code, Address re_frame); + private: // Offsets from frame_pointer() of function parameters and stored registers. static const int kFramePointer = 0; diff --git a/src/mips/simulator-mips.cc b/src/mips/simulator-mips.cc index 3b386953..17c18977 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 @@ int32_t Simulator::get_pc() const { 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 @@ int Simulator::ReadW(int32_t addr, Instruction* instr) { 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(); } @@ -2716,7 +2721,7 @@ int32_t Simulator::Call(byte* entry, int argument_count, ...) { // Store remaining arguments on stack, from low to high memory. intptr_t* stack_argument = reinterpret_cast<intptr_t*>(entry_stack); for (int i = 4; i < argument_count; i++) { - stack_argument[i - 4 + kArgsSlotsNum] = va_arg(parameters, int32_t); + stack_argument[i - 4 + kCArgSlotCount] = va_arg(parameters, int32_t); } va_end(parameters); set_register(sp, entry_stack); diff --git a/src/mips/stub-cache-mips.cc b/src/mips/stub-cache-mips.cc index c17a658d..5b949734 100644 --- a/src/mips/stub-cache-mips.cc +++ b/src/mips/stub-cache-mips.cc @@ -3099,7 +3099,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { // -- a1 : receiver // ----------------------------------- Code* stub; - JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + ElementsKind elements_kind = receiver_map->elements_kind(); MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(a1, @@ -3191,7 +3191,7 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { // -- a3 : scratch // ----------------------------------- Code* stub; - JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + ElementsKind elements_kind = receiver_map->elements_kind(); bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; MaybeObject* maybe_stub = KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); @@ -3442,25 +3442,25 @@ void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( } -static bool IsElementTypeSigned(JSObject::ElementsKind elements_kind) { +static bool IsElementTypeSigned(ElementsKind elements_kind) { switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_INT_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: return true; - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: return false; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); return false; } @@ -3470,7 +3470,7 @@ static bool IsElementTypeSigned(JSObject::ElementsKind elements_kind) { void KeyedLoadStubCompiler::GenerateLoadExternalArray( MacroAssembler* masm, - JSObject::ElementsKind elements_kind) { + ElementsKind elements_kind) { // ---------- S t a t e -------------- // -- ra : return address // -- a0 : key @@ -3501,36 +3501,36 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( // We are not untagging smi key and instead work with it // as if it was premultiplied by 2. - ASSERT((kSmiTag == 0) && (kSmiTagSize == 1)); + STATIC_ASSERT((kSmiTag == 0) && (kSmiTagSize == 1)); Register value = a2; switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: __ srl(t2, key, 1); __ addu(t3, a3, t2); __ lb(value, MemOperand(t3, 0)); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ srl(t2, key, 1); __ addu(t3, a3, t2); __ lbu(value, MemOperand(t3, 0)); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: __ addu(t3, a3, key); __ lh(value, MemOperand(t3, 0)); break; - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ addu(t3, a3, key); __ lhu(value, MemOperand(t3, 0)); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ sll(t2, key, 1); __ addu(t3, a3, t2); __ lw(value, MemOperand(t3, 0)); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: __ sll(t3, t2, 2); __ addu(t3, a3, t3); if (CpuFeatures::IsSupported(FPU)) { @@ -3540,7 +3540,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ lw(value, MemOperand(t3, 0)); } break; - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: __ sll(t2, key, 2); __ addu(t3, a3, t2); if (CpuFeatures::IsSupported(FPU)) { @@ -3552,10 +3552,10 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ lw(a3, MemOperand(t3, Register::kSizeInBytes)); } break; - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -3569,7 +3569,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( // f0: value (if FPU is supported) // a2/a3: value (if FPU is not supported) - if (elements_kind == JSObject::EXTERNAL_INT_ELEMENTS) { + if (elements_kind == EXTERNAL_INT_ELEMENTS) { // For the Int and UnsignedInt array types, we need to see whether // the value can be represented in a Smi. If not, we need to convert // it to a HeapNumber. @@ -3611,7 +3611,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ sw(dst2, FieldMemOperand(v0, HeapNumber::kExponentOffset)); __ Ret(); } - } else if (elements_kind == JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS) { + } else if (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) { // The test is different for unsigned int values. Since we need // the value to be in the range of a positive smi, we can't // handle either of the top two bits being set in the value. @@ -3682,7 +3682,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ mov(v0, t2); __ Ret(); } - } else if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + } else if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { // For the floating-point array type, we need to always allocate a // HeapNumber. if (CpuFeatures::IsSupported(FPU)) { @@ -3749,7 +3749,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ Ret(); } - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { if (CpuFeatures::IsSupported(FPU)) { CpuFeatures::Scope scope(FPU); // Allocate a HeapNumber for the result. Don't use a0 and a1 as @@ -3803,7 +3803,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( void KeyedStoreStubCompiler::GenerateStoreExternalArray( MacroAssembler* masm, - JSObject::ElementsKind elements_kind) { + ElementsKind elements_kind) { // ---------- S t a t e -------------- // -- a0 : value // -- a1 : key @@ -3838,7 +3838,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // a3: external array. // t0: key (integer). - if (elements_kind == JSObject::EXTERNAL_PIXEL_ELEMENTS) { + if (elements_kind == EXTERNAL_PIXEL_ELEMENTS) { // Double to pixel conversion is only implemented in the runtime for now. __ JumpIfNotSmi(value, &slow); } else { @@ -3852,7 +3852,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // t1: value (integer). switch (elements_kind) { - case JSObject::EXTERNAL_PIXEL_ELEMENTS: { + case EXTERNAL_PIXEL_ELEMENTS: { // Clamp the value to [0..255]. // v0 is used as a scratch register here. Label done; @@ -3869,28 +3869,28 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ sb(t1, MemOperand(t8, 0)); } break; - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ addu(t8, a3, t0); __ sb(t1, MemOperand(t8, 0)); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ sll(t8, t0, 1); __ addu(t8, a3, t8); __ sh(t1, MemOperand(t8, 0)); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ sll(t8, t0, 2); __ addu(t8, a3, t8); __ sw(t1, MemOperand(t8, 0)); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: // Perform int-to-float conversion and store to memory. StoreIntAsFloat(masm, a3, t0, t1, t2, t3, t4); break; - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: __ sll(t8, t0, 3); __ addu(a3, a3, t8); // a3: effective address of the double element @@ -3912,10 +3912,10 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ sw(t3, MemOperand(a3, Register::kSizeInBytes)); } break; - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -3924,7 +3924,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ mov(v0, value); __ Ret(); - if (elements_kind != JSObject::EXTERNAL_PIXEL_ELEMENTS) { + if (elements_kind != EXTERNAL_PIXEL_ELEMENTS) { // a3: external array. // t0: index (integer). __ bind(&check_heap_number); @@ -3945,12 +3945,12 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ ldc1(f0, FieldMemOperand(a0, HeapNumber::kValueOffset)); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { __ cvt_s_d(f0, f0); __ sll(t8, t0, 2); __ addu(t8, a3, t8); __ swc1(f0, MemOperand(t8, 0)); - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { __ sll(t8, t0, 3); __ addu(t8, a3, t8); __ sdc1(f0, MemOperand(t8, 0)); @@ -3958,30 +3958,30 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ EmitECMATruncate(t3, f0, f2, t2, t1, t5); switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ addu(t8, a3, t0); __ sb(t3, MemOperand(t8, 0)); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ sll(t8, t0, 1); __ addu(t8, a3, t8); __ sh(t3, MemOperand(t8, 0)); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ sll(t8, t0, 2); __ addu(t8, a3, t8); __ sw(t3, MemOperand(t8, 0)); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -3997,7 +3997,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ lw(t3, FieldMemOperand(value, HeapNumber::kExponentOffset)); __ lw(t4, FieldMemOperand(value, HeapNumber::kMantissaOffset)); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { Label done, nan_or_infinity_or_zero; static const int kMantissaInHiWordShift = kBinary32MantissaBits - HeapNumber::kMantissaBitsInTopWord; @@ -4062,7 +4062,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ srl(t4, t4, kMantissaInLoWordShift); __ or_(t3, t6, t4); __ Branch(&done); - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { __ sll(t8, t0, 3); __ addu(t8, a3, t8); // t8: effective address of destination element. @@ -4128,30 +4128,30 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // Result is in t3. // This switch block should be exactly the same as above (FPU mode). switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ addu(t8, a3, t0); __ sb(t3, MemOperand(t8, 0)); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ sll(t8, t0, 1); __ addu(t8, a3, t8); __ sh(t3, MemOperand(t8, 0)); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ sll(t8, t0, 2); __ addu(t8, a3, t8); __ sw(t3, MemOperand(t8, 0)); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -4213,7 +4213,7 @@ void KeyedLoadStubCompiler::GenerateLoadFastElement(MacroAssembler* masm) { // Load the result and make sure it's not the hole. __ Addu(a3, a2, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); - ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); __ sll(t0, a0, kPointerSizeLog2 - kSmiTagSize); __ Addu(t0, t0, a3); __ lw(t0, MemOperand(t0)); @@ -4344,7 +4344,7 @@ void KeyedStoreStubCompiler::GenerateStoreFastElement(MacroAssembler* masm, __ Addu(scratch, elements_reg, Operand(FixedArray::kHeaderSize - kHeapObjectTag)); - ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); + STATIC_ASSERT(kSmiTag == 0 && kSmiTagSize < kPointerSizeLog2); __ sll(scratch2, key_reg, kPointerSizeLog2 - kSmiTagSize); __ Addu(scratch3, scratch2, scratch); __ sw(value_reg, MemOperand(scratch3)); diff --git a/src/mksnapshot.cc b/src/mksnapshot.cc index 4f5fe96a..a791dbba 100644 --- a/src/mksnapshot.cc +++ b/src/mksnapshot.cc @@ -29,8 +29,6 @@ #include <bzlib.h> #endif #include <signal.h> -#include <string> -#include <map> #include "v8.h" @@ -86,16 +84,6 @@ class CounterCollection { }; -// We statically allocate a set of local counters to be used if we -// don't want to store the stats in a memory-mapped file -static CounterCollection local_counters; - - -typedef std::map<std::string, int*> CounterMap; -typedef std::map<std::string, int*>::iterator CounterMapIterator; -static CounterMap counter_table_; - - class Compressor { public: virtual ~Compressor() {} diff --git a/src/objects-debug.cc b/src/objects-debug.cc index 4da360b8..8de7162a 100644 --- a/src/objects-debug.cc +++ b/src/objects-debug.cc @@ -164,6 +164,9 @@ void HeapObject::HeapObjectVerify() { 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; @@ -257,9 +260,9 @@ void JSObject::JSObjectVerify() { (map()->inobject_properties() + properties()->length() - map()->NextFreePropertyIndex())); } - ASSERT(map()->has_fast_elements() == - (elements()->map() == GetHeap()->fixed_array_map() || - elements()->map() == GetHeap()->fixed_cow_array_map())); + ASSERT_EQ(map()->has_fast_elements(), + (elements()->map() == GetHeap()->fixed_array_map() || + elements()->map() == GetHeap()->fixed_cow_array_map())); ASSERT(map()->has_fast_elements() == HasFastElements()); } @@ -536,6 +539,15 @@ void JSProxy::JSProxyVerify() { 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 ff3be03b..8796865c 100644 --- a/src/objects-inl.h +++ b/src/objects-inl.h @@ -2004,7 +2004,7 @@ bool DescriptorArray::IsProperty(int descriptor_number) { bool DescriptorArray::IsTransition(int descriptor_number) { PropertyType t = GetType(descriptor_number); return t == MAP_TRANSITION || t == CONSTANT_TRANSITION || - t == EXTERNAL_ARRAY_TRANSITION; + t == ELEMENTS_TRANSITION; } @@ -2871,7 +2871,7 @@ Code::Flags Code::flags() { void Code::set_flags(Code::Flags flags) { - STATIC_ASSERT(Code::NUMBER_OF_KINDS <= (kFlagsKindMask >> kFlagsKindShift)+1); + STATIC_ASSERT(Code::NUMBER_OF_KINDS <= KindField::kMax + 1); // Make sure that all call stubs have an arguments count. ASSERT((ExtractKindFromFlags(flags) != CALL_IC && ExtractKindFromFlags(flags) != KEYED_CALL_IC) || @@ -2885,11 +2885,6 @@ Code::Kind Code::kind() { } -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 @@ -2955,13 +2950,31 @@ void Code::set_optimizable(bool value) { 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); } @@ -3095,7 +3108,6 @@ bool Code::is_inline_cache_stub() { Code::Flags Code::ComputeFlags(Kind kind, - InLoopFlag in_loop, InlineCacheState ic_state, ExtraICState extra_ic_state, PropertyType type, @@ -3104,26 +3116,17 @@ Code::Flags Code::ComputeFlags(Kind kind, // 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 = kind << kFlagsKindShift; - if (in_loop) bits |= kFlagsICInLoopMask; - bits |= ic_state << kFlagsICStateShift; - bits |= type << kFlagsTypeShift; - bits |= extra_ic_state << kFlagsExtraICStateShift; - bits |= argc << kFlagsArgumentsCountShift; - if (holder == PROTOTYPE_MAP) bits |= kFlagsCacheInPrototypeMapMask; - // Cast to flags and validate result before returning it. - Flags result = static_cast<Flags>(bits); - ASSERT(ExtractKindFromFlags(result) == kind); - ASSERT(ExtractICStateFromFlags(result) == ic_state); - ASSERT(ExtractICInLoopFromFlags(result) == in_loop); - ASSERT(ExtractTypeFromFlags(result) == type); - ASSERT(ExtractExtraICStateFromFlags(result) == extra_ic_state); - ASSERT(ExtractArgumentsCountFromFlags(result) == argc); - return result; + int bits = KindField::encode(kind) + | ICStateField::encode(ic_state) + | TypeField::encode(type) + | ExtraICStateField::encode(extra_ic_state) + | (argc << kArgumentsCountShift) + | CacheHolderField::encode(holder); + return static_cast<Flags>(bits); } @@ -3131,56 +3134,43 @@ Code::Flags Code::ComputeMonomorphicFlags(Kind kind, 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); } Code::Kind Code::ExtractKindFromFlags(Flags flags) { - int bits = (flags & kFlagsKindMask) >> kFlagsKindShift; - return static_cast<Kind>(bits); + return KindField::decode(flags); } InlineCacheState Code::ExtractICStateFromFlags(Flags flags) { - int bits = (flags & kFlagsICStateMask) >> kFlagsICStateShift; - return static_cast<InlineCacheState>(bits); + return ICStateField::decode(flags); } Code::ExtraICState Code::ExtractExtraICStateFromFlags(Flags flags) { - int bits = (flags & kFlagsExtraICStateMask) >> kFlagsExtraICStateShift; - return static_cast<ExtraICState>(bits); -} - - -InLoopFlag Code::ExtractICInLoopFromFlags(Flags flags) { - int bits = (flags & kFlagsICInLoopMask); - return bits != 0 ? IN_LOOP : NOT_IN_LOOP; + return ExtraICStateField::decode(flags); } PropertyType Code::ExtractTypeFromFlags(Flags flags) { - int bits = (flags & kFlagsTypeMask) >> kFlagsTypeShift; - return static_cast<PropertyType>(bits); + return TypeField::decode(flags); } int Code::ExtractArgumentsCountFromFlags(Flags flags) { - return (flags & kFlagsArgumentsCountMask) >> kFlagsArgumentsCountShift; + return (flags & kArgumentsCountMask) >> kArgumentsCountShift; } InlineCacheHolderFlag Code::ExtractCacheHolderFromFlags(Flags flags) { - int bits = (flags & kFlagsCacheInPrototypeMapMask); - return bits != 0 ? PROTOTYPE_MAP : OWN_MAP; + return CacheHolderField::decode(flags); } Code::Flags Code::RemoveTypeFromFlags(Flags flags) { - int bits = flags & ~kFlagsTypeMask; + int bits = flags & ~TypeField::kMask; return static_cast<Flags>(bits); } @@ -3263,7 +3253,7 @@ MaybeObject* Map::GetFastElementsMap() { if (!maybe_obj->ToObject(&obj)) return maybe_obj; } Map* new_map = Map::cast(obj); - new_map->set_elements_kind(JSObject::FAST_ELEMENTS); + new_map->set_elements_kind(FAST_ELEMENTS); isolate()->counters()->map_to_fast_elements()->Increment(); return new_map; } @@ -3276,7 +3266,7 @@ MaybeObject* Map::GetFastDoubleElementsMap() { if (!maybe_obj->ToObject(&obj)) return maybe_obj; } Map* new_map = Map::cast(obj); - new_map->set_elements_kind(JSObject::FAST_DOUBLE_ELEMENTS); + new_map->set_elements_kind(FAST_DOUBLE_ELEMENTS); isolate()->counters()->map_to_fast_double_elements()->Increment(); return new_map; } @@ -3289,7 +3279,7 @@ MaybeObject* Map::GetSlowElementsMap() { if (!maybe_obj->ToObject(&obj)) return maybe_obj; } Map* new_map = Map::cast(obj); - new_map->set_elements_kind(JSObject::DICTIONARY_ELEMENTS); + new_map->set_elements_kind(DICTIONARY_ELEMENTS); isolate()->counters()->map_to_slow_elements()->Increment(); return new_map; } @@ -3920,7 +3910,16 @@ void JSBuiltinsObject::set_javascript_builtin_code(Builtins::JavaScript id, 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) @@ -4102,7 +4101,7 @@ void JSRegExp::SetDataAtUnchecked(int index, Object* value, Heap* heap) { } -JSObject::ElementsKind JSObject::GetElementsKind() { +ElementsKind JSObject::GetElementsKind() { ElementsKind kind = map()->elements_kind(); ASSERT((kind == FAST_ELEMENTS && (elements()->map() == GetHeap()->fixed_array_map() || @@ -4441,9 +4440,7 @@ PropertyAttributes AccessorInfo::property_attributes() { void AccessorInfo::set_property_attributes(PropertyAttributes attributes) { - ASSERT(AttributesField::is_valid(attributes)); - int rest_value = flag()->value() & ~AttributesField::mask(); - set_flag(Smi::FromInt(rest_value | AttributesField::encode(attributes))); + set_flag(Smi::FromInt(AttributesField::update(flag()->value(), attributes))); } diff --git a/src/objects-printer.cc b/src/objects-printer.cc index 35735724..0398572f 100644 --- a/src/objects-printer.cc +++ b/src/objects-printer.cc @@ -151,6 +151,9 @@ void HeapObject::HeapObjectPrint(FILE* out) { 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 JSProxy::JSProxyPrint(FILE* out) { } +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 bde9e831..0aa21dd6 100644 --- a/src/objects-visiting.cc +++ b/src/objects-visiting.cc @@ -105,6 +105,11 @@ StaticVisitorBase::VisitorId StaticVisitorBase::GetVisitorId( 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 0660dbaa..6085b4ef 100644 --- a/src/objects.cc +++ b/src/objects.cc @@ -41,7 +41,6 @@ #include "objects-visiting.h" #include "macro-assembler.h" #include "safepoint-table.h" -#include "scanner-base.h" #include "string-stream.h" #include "utils.h" #include "vm-state-inl.h" @@ -85,7 +84,7 @@ MaybeObject* Object::ToObject(Context* global_context) { MaybeObject* Object::ToObject() { - if (IsJSObject()) { + if (IsJSReceiver()) { return this; } else if (IsNumber()) { Isolate* isolate = Isolate::Current(); @@ -238,6 +237,7 @@ MaybeObject* Object::GetPropertyWithHandler(Object* receiver_raw, // 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(); @@ -592,7 +592,7 @@ MaybeObject* Object::GetProperty(Object* receiver, return holder->GetPropertyWithInterceptor(recvr, name, attributes); } case MAP_TRANSITION: - case EXTERNAL_ARRAY_TRANSITION: + case ELEMENTS_TRANSITION: case CONSTANT_TRANSITION: case NULL_DESCRIPTOR: break; @@ -628,6 +628,7 @@ MaybeObject* Object::GetElementWithReceiver(Object* receiver, uint32_t index) { } 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. @@ -1048,6 +1049,7 @@ void JSObject::JSObjectShortPrint(StringStream* accumulator) { global_object ? "Global Object: " : "", vowel ? "n" : ""); accumulator->Put(str); + accumulator->Put('>'); printed = true; } } @@ -1173,6 +1175,12 @@ void HeapObject::HeapObjectShortPrint(StringStream* accumulator) { 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 @@ void HeapObject::IterateBody(InstanceType type, int object_size, 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; @@ -1435,13 +1446,12 @@ MaybeObject* JSObject::AddFastProperty(String* name, // it's unrelated to properties. int descriptor_index = old_descriptors->Search(name); - // External array transitions are stored in the descriptor for property "", - // which is not a identifier and should have forced a switch to slow - // properties above. + // Element transitions are stored in the descriptor for property "", which is + // not a identifier and should have forced a switch to slow properties above. ASSERT(descriptor_index == DescriptorArray::kNotFound || - old_descriptors->GetType(descriptor_index) != EXTERNAL_ARRAY_TRANSITION); + old_descriptors->GetType(descriptor_index) != ELEMENTS_TRANSITION); bool can_insert_transition = descriptor_index == DescriptorArray::kNotFound || - old_descriptors->GetType(descriptor_index) == EXTERNAL_ARRAY_TRANSITION; + old_descriptors->GetType(descriptor_index) == ELEMENTS_TRANSITION; bool allow_map_transition = can_insert_transition && (isolate->context()->global_context()->object_function()->map() != map()); @@ -1989,61 +1999,25 @@ void Map::LookupInDescriptors(JSObject* holder, } -static JSObject::ElementsKind GetElementsKindFromExternalArrayType( - ExternalArrayType array_type) { - switch (array_type) { - case kExternalByteArray: - return JSObject::EXTERNAL_BYTE_ELEMENTS; - break; - case kExternalUnsignedByteArray: - return JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS; - break; - case kExternalShortArray: - return JSObject::EXTERNAL_SHORT_ELEMENTS; - break; - case kExternalUnsignedShortArray: - return JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS; - break; - case kExternalIntArray: - return JSObject::EXTERNAL_INT_ELEMENTS; - break; - case kExternalUnsignedIntArray: - return JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS; - break; - case kExternalFloatArray: - return JSObject::EXTERNAL_FLOAT_ELEMENTS; - break; - case kExternalDoubleArray: - return JSObject::EXTERNAL_DOUBLE_ELEMENTS; - break; - case kExternalPixelArray: - return JSObject::EXTERNAL_PIXEL_ELEMENTS; - break; - } - UNREACHABLE(); - return JSObject::DICTIONARY_ELEMENTS; -} - - -MaybeObject* Map::GetExternalArrayElementsMap(ExternalArrayType array_type, - bool safe_to_add_transition) { +MaybeObject* Map::GetElementsTransitionMap(ElementsKind elements_kind, + bool safe_to_add_transition) { Heap* current_heap = heap(); DescriptorArray* descriptors = instance_descriptors(); - String* external_array_sentinel_name = current_heap->empty_symbol(); + String* elements_transition_sentinel_name = current_heap->empty_symbol(); if (safe_to_add_transition) { // It's only safe to manipulate the descriptor array if it would be // safe to add a transition. ASSERT(!is_shared()); // no transitions can be added to shared maps. - // Check if the external array transition already exists. + // Check if the elements transition already exists. DescriptorLookupCache* cache = current_heap->isolate()->descriptor_lookup_cache(); - int index = cache->Lookup(descriptors, external_array_sentinel_name); + int index = cache->Lookup(descriptors, elements_transition_sentinel_name); if (index == DescriptorLookupCache::kAbsent) { - index = descriptors->Search(external_array_sentinel_name); + index = descriptors->Search(elements_transition_sentinel_name); cache->Update(descriptors, - external_array_sentinel_name, + elements_transition_sentinel_name, index); } @@ -2051,8 +2025,8 @@ MaybeObject* Map::GetExternalArrayElementsMap(ExternalArrayType array_type, // return it. if (index != DescriptorArray::kNotFound) { PropertyDetails details(PropertyDetails(descriptors->GetDetails(index))); - if (details.type() == EXTERNAL_ARRAY_TRANSITION && - details.array_type() == array_type) { + if (details.type() == ELEMENTS_TRANSITION && + details.elements_kind() == elements_kind) { return descriptors->GetValue(index); } else { safe_to_add_transition = false; @@ -2060,28 +2034,29 @@ MaybeObject* Map::GetExternalArrayElementsMap(ExternalArrayType array_type, } } - // No transition to an existing external array map. Make a new one. + // No transition to an existing map for the given ElementsKind. Make a new + // one. Object* obj; { MaybeObject* maybe_map = CopyDropTransitions(); if (!maybe_map->ToObject(&obj)) return maybe_map; } Map* new_map = Map::cast(obj); - new_map->set_elements_kind(GetElementsKindFromExternalArrayType(array_type)); + new_map->set_elements_kind(elements_kind); GetIsolate()->counters()->map_to_external_array_elements()->Increment(); // Only remember the map transition if the object's map is NOT equal to the // global object_function's map and there is not an already existing - // non-matching external array transition. + // non-matching element transition. bool allow_map_transition = safe_to_add_transition && (GetIsolate()->context()->global_context()->object_function()->map() != map()); if (allow_map_transition) { // Allocate new instance descriptors for the old map with map transition. - ExternalArrayTransitionDescriptor desc(external_array_sentinel_name, - Map::cast(new_map), - array_type); + ElementsTransitionDescriptor desc(elements_transition_sentinel_name, + Map::cast(new_map), + elements_kind); Object* new_descriptors; MaybeObject* maybe_new_descriptors = descriptors->CopyInsert( &desc, @@ -2248,6 +2223,7 @@ bool JSProxy::HasPropertyWithHandler(String* name_raw) { // 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(); } @@ -2278,6 +2254,7 @@ MUST_USE_RESULT MaybeObject* JSProxy::SetPropertyWithHandler( // 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(); } @@ -2305,6 +2282,7 @@ MUST_USE_RESULT MaybeObject* JSProxy::DeletePropertyWithHandler( // 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( @@ -2347,6 +2325,7 @@ MUST_USE_RESULT PropertyAttributes JSProxy::GetPropertyAttributeWithHandler( 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( @@ -2373,9 +2352,13 @@ void JSProxy::Fix() { 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. } @@ -2498,7 +2481,7 @@ MaybeObject* JSObject::SetPropertyForResult(LookupResult* result, return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); } case NULL_DESCRIPTOR: - case EXTERNAL_ARRAY_TRANSITION: + case ELEMENTS_TRANSITION: return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); default: UNREACHABLE(); @@ -2586,7 +2569,7 @@ MaybeObject* JSObject::SetLocalPropertyIgnoreAttributes( // if the value is a function. return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); case NULL_DESCRIPTOR: - case EXTERNAL_ARRAY_TRANSITION: + case ELEMENTS_TRANSITION: return ConvertDescriptorToFieldAndMapTransition(name, value, attributes); default: UNREACHABLE(); @@ -2867,7 +2850,7 @@ MaybeObject* JSObject::NormalizeProperties(PropertyNormalizationMode mode, case CONSTANT_TRANSITION: case NULL_DESCRIPTOR: case INTERCEPTOR: - case EXTERNAL_ARRAY_TRANSITION: + case ELEMENTS_TRANSITION: break; default: UNREACHABLE(); @@ -5100,13 +5083,13 @@ String::FlatContent String::GetFlatContent() { } -SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls, - RobustnessFlag robust_flag, - int offset, - int length, - int* length_return) { +SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, + RobustnessFlag robust_flag, + int offset, + int length, + int* length_return) { if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { - return SmartPointer<char>(NULL); + return SmartArrayPointer<char>(NULL); } Heap* heap = GetHeap(); @@ -5150,13 +5133,13 @@ SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls, character_position++; } result[utf8_byte_position] = 0; - return SmartPointer<char>(result); + return SmartArrayPointer<char>(result); } -SmartPointer<char> String::ToCString(AllowNullsFlag allow_nulls, - RobustnessFlag robust_flag, - int* length_return) { +SmartArrayPointer<char> String::ToCString(AllowNullsFlag allow_nulls, + RobustnessFlag robust_flag, + int* length_return) { return ToCString(allow_nulls, robust_flag, 0, -1, length_return); } @@ -5187,9 +5170,9 @@ const uc16* String::GetTwoByteData(unsigned start) { } -SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) { +SmartArrayPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) { if (robust_flag == ROBUST_STRING_TRAVERSAL && !LooksValid()) { - return SmartPointer<uc16>(); + return SmartArrayPointer<uc16>(); } Heap* heap = GetHeap(); @@ -5205,7 +5188,7 @@ SmartPointer<uc16> String::ToWideCString(RobustnessFlag robust_flag) { result[i++] = character; } result[i] = 0; - return SmartPointer<uc16>(result); + return SmartArrayPointer<uc16>(result); } @@ -6219,7 +6202,7 @@ void Map::CreateBackPointers() { DescriptorArray* descriptors = instance_descriptors(); for (int i = 0; i < descriptors->number_of_descriptors(); i++) { if (descriptors->GetType(i) == MAP_TRANSITION || - descriptors->GetType(i) == EXTERNAL_ARRAY_TRANSITION || + descriptors->GetType(i) == ELEMENTS_TRANSITION || descriptors->GetType(i) == CONSTANT_TRANSITION) { // Get target. Map* target = Map::cast(descriptors->GetValue(i)); @@ -6262,7 +6245,7 @@ void Map::ClearNonLiveTransitions(Heap* heap, Object* real_prototype) { // non-live object. PropertyDetails details(Smi::cast(contents->get(i + 1))); if (details.type() == MAP_TRANSITION || - details.type() == EXTERNAL_ARRAY_TRANSITION || + details.type() == ELEMENTS_TRANSITION || details.type() == CONSTANT_TRANSITION) { Map* target = reinterpret_cast<Map*>(contents->get(i)); ASSERT(target->IsHeapObject()); @@ -6422,7 +6405,7 @@ Object* JSFunction::SetInstanceClassName(String* name) { void JSFunction::PrintName(FILE* out) { - SmartPointer<char> name = shared()->DebugName()->ToCString(); + SmartArrayPointer<char> name = shared()->DebugName()->ToCString(); PrintF(out, "%s", *name); } @@ -7026,7 +7009,7 @@ void DeoptimizationInputData::DeoptimizationInputDataPrint(FILE* out) { JSFunction* function = JSFunction::cast(LiteralArray()->get(function_id)); unsigned height = iterator.Next(); - PrintF(out, "{ast_id=%d, \nfunction=", ast_id); + PrintF(out, "{ast_id=%d, function=", ast_id); function->PrintName(out); PrintF(out, ", height=%u}", height); break; @@ -7151,7 +7134,7 @@ const char* Code::PropertyType2String(PropertyType type) { case HANDLER: return "HANDLER"; case INTERCEPTOR: return "INTERCEPTOR"; case MAP_TRANSITION: return "MAP_TRANSITION"; - case EXTERNAL_ARRAY_TRANSITION: return "EXTERNAL_ARRAY_TRANSITION"; + case ELEMENTS_TRANSITION: return "ELEMENTS_TRANSITION"; case CONSTANT_TRANSITION: return "CONSTANT_TRANSITION"; case NULL_DESCRIPTOR: return "NULL_DESCRIPTOR"; } @@ -7190,7 +7173,6 @@ void Code::Disassemble(const char* name, FILE* out) { 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())); } @@ -7541,7 +7523,7 @@ MaybeObject* JSObject::SetElementsLength(Object* len) { if (maybe_smi_length->ToObject(&smi_length) && smi_length->IsSmi()) { const int value = Smi::cast(smi_length)->value(); if (value < 0) return ArrayLengthRangeError(GetHeap()); - JSObject::ElementsKind elements_kind = GetElementsKind(); + ElementsKind elements_kind = GetElementsKind(); switch (elements_kind) { case FAST_ELEMENTS: case FAST_DOUBLE_ELEMENTS: { @@ -10151,6 +10133,8 @@ MaybeObject* JSObject::PrepareSlowElementsForSort(uint32_t limit) { // If the object is in dictionary mode, it is converted to fast elements // mode. MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { + ASSERT(!HasExternalArrayElements()); + Heap* heap = GetHeap(); if (HasDictionaryElements()) { @@ -10180,9 +10164,6 @@ MaybeObject* JSObject::PrepareElementsForSort(uint32_t limit) { set_map(new_map); set_elements(fast_elements); - } else if (HasExternalArrayElements()) { - // External arrays cannot have holes or undefined elements. - return Smi::FromInt(ExternalArray::cast(elements())->length()); } else if (!HasFastDoubleElements()) { Object* obj; { MaybeObject* maybe_obj = EnsureWritableFastElements(); diff --git a/src/objects.h b/src/objects.h index 53ba981c..d9c7a822 100644 --- a/src/objects.h +++ b/src/objects.h @@ -31,7 +31,7 @@ #include "allocation.h" #include "builtins.h" #include "list.h" -#include "smart-pointer.h" +#include "smart-array-pointer.h" #include "unicode-inl.h" #if V8_TARGET_ARCH_ARM #include "arm/constants-arm.h" @@ -135,6 +135,37 @@ enum PropertyAttributes { namespace v8 { namespace internal { +enum ElementsKind { + // The "fast" kind for tagged values. Must be first to make it possible + // to efficiently check maps if they have fast elements. + FAST_ELEMENTS, + + // The "fast" kind for unwrapped, non-tagged double values. + FAST_DOUBLE_ELEMENTS, + + // The "slow" kind. + DICTIONARY_ELEMENTS, + NON_STRICT_ARGUMENTS_ELEMENTS, + // The "fast" kind for external arrays + EXTERNAL_BYTE_ELEMENTS, + EXTERNAL_UNSIGNED_BYTE_ELEMENTS, + EXTERNAL_SHORT_ELEMENTS, + EXTERNAL_UNSIGNED_SHORT_ELEMENTS, + EXTERNAL_INT_ELEMENTS, + EXTERNAL_UNSIGNED_INT_ELEMENTS, + EXTERNAL_FLOAT_ELEMENTS, + EXTERNAL_DOUBLE_ELEMENTS, + EXTERNAL_PIXEL_ELEMENTS, + + // Derived constants from ElementsKind + FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_BYTE_ELEMENTS, + LAST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS, + FIRST_ELEMENTS_KIND = FAST_ELEMENTS, + LAST_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS +}; + +static const int kElementsKindCount = + LAST_ELEMENTS_KIND - FIRST_ELEMENTS_KIND + 1; // PropertyDetails captures type and attributes for a property. // They are used both in property dictionaries and instance descriptors. @@ -143,7 +174,7 @@ class PropertyDetails BASE_EMBEDDED { PropertyDetails(PropertyAttributes attributes, PropertyType type, int index = 0) { - ASSERT(type != EXTERNAL_ARRAY_TRANSITION); + ASSERT(type != ELEMENTS_TRANSITION); ASSERT(TypeField::is_valid(type)); ASSERT(AttributesField::is_valid(attributes)); ASSERT(StorageField::is_valid(index)); @@ -159,19 +190,19 @@ class PropertyDetails BASE_EMBEDDED { PropertyDetails(PropertyAttributes attributes, PropertyType type, - ExternalArrayType array_type) { - ASSERT(type == EXTERNAL_ARRAY_TRANSITION); + ElementsKind elements_kind) { + ASSERT(type == ELEMENTS_TRANSITION); ASSERT(TypeField::is_valid(type)); ASSERT(AttributesField::is_valid(attributes)); - ASSERT(StorageField::is_valid(static_cast<int>(array_type))); + ASSERT(StorageField::is_valid(static_cast<int>(elements_kind))); value_ = TypeField::encode(type) | AttributesField::encode(attributes) - | StorageField::encode(static_cast<int>(array_type)); + | StorageField::encode(static_cast<int>(elements_kind)); ASSERT(type == this->type()); ASSERT(attributes == this->attributes()); - ASSERT(array_type == this->array_type()); + ASSERT(elements_kind == this->elements_kind()); } // Conversion for storing details as Object*. @@ -184,7 +215,7 @@ class PropertyDetails BASE_EMBEDDED { PropertyType t = type(); ASSERT(t != INTERCEPTOR); return t == MAP_TRANSITION || t == CONSTANT_TRANSITION || - t == EXTERNAL_ARRAY_TRANSITION; + t == ELEMENTS_TRANSITION; } bool IsProperty() { @@ -195,9 +226,9 @@ class PropertyDetails BASE_EMBEDDED { int index() { return StorageField::decode(value_); } - ExternalArrayType array_type() { - ASSERT(type() == EXTERNAL_ARRAY_TRANSITION); - return static_cast<ExternalArrayType>(StorageField::decode(value_)); + ElementsKind elements_kind() { + ASSERT(type() == ELEMENTS_TRANSITION); + return static_cast<ElementsKind>(StorageField::decode(value_)); } inline PropertyDetails AsDeleted(); @@ -1463,38 +1494,6 @@ class JSReceiver: public HeapObject { // caching. class JSObject: public JSReceiver { public: - enum ElementsKind { - // The "fast" kind for tagged values. Must be first to make it possible - // to efficiently check maps if they have fast elements. - FAST_ELEMENTS, - - // The "fast" kind for unwrapped, non-tagged double values. - FAST_DOUBLE_ELEMENTS, - - // The "slow" kind. - DICTIONARY_ELEMENTS, - NON_STRICT_ARGUMENTS_ELEMENTS, - // The "fast" kind for external arrays - EXTERNAL_BYTE_ELEMENTS, - EXTERNAL_UNSIGNED_BYTE_ELEMENTS, - EXTERNAL_SHORT_ELEMENTS, - EXTERNAL_UNSIGNED_SHORT_ELEMENTS, - EXTERNAL_INT_ELEMENTS, - EXTERNAL_UNSIGNED_INT_ELEMENTS, - EXTERNAL_FLOAT_ELEMENTS, - EXTERNAL_DOUBLE_ELEMENTS, - EXTERNAL_PIXEL_ELEMENTS, - - // Derived constants from ElementsKind - FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_BYTE_ELEMENTS, - LAST_EXTERNAL_ARRAY_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS, - FIRST_ELEMENTS_KIND = FAST_ELEMENTS, - LAST_ELEMENTS_KIND = EXTERNAL_PIXEL_ELEMENTS - }; - - static const int kElementsKindCount = - LAST_ELEMENTS_KIND - FIRST_ELEMENTS_KIND + 1; - // [properties]: Backing storage for properties. // properties is a FixedArray in the fast case and a Dictionary in the // slow case. @@ -3154,7 +3153,6 @@ class ByteArray: public FixedArrayBase { // raised rather than being silently ignored. class ExternalArray: public FixedArrayBase { public: - inline bool is_the_hole(int index) { return false; } // [external_pointer]: The pointer to the external memory area backing this @@ -3655,7 +3653,6 @@ class Code: public HeapObject { 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. @@ -3685,6 +3682,11 @@ class Code: public HeapObject { 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 @@ -3743,7 +3745,6 @@ class Code: public HeapObject { // 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, @@ -3755,16 +3756,15 @@ class Code: public HeapObject { PropertyType type, ExtraICState extra_ic_state = kNoExtraICState, InlineCacheHolderFlag holder = OWN_MAP, - InLoopFlag in_loop = NOT_IN_LOOP, int argc = -1); - static inline Kind ExtractKindFromFlags(Flags flags); static inline InlineCacheState ExtractICStateFromFlags(Flags flags); - static inline ExtraICState ExtractExtraICStateFromFlags(Flags flags); - static inline InLoopFlag ExtractICInLoopFromFlags(Flags flags); static inline PropertyType ExtractTypeFromFlags(Flags flags); - static inline int ExtractArgumentsCountFromFlags(Flags flags); + static inline Kind ExtractKindFromFlags(Flags flags); static inline InlineCacheHolderFlag ExtractCacheHolderFromFlags(Flags flags); + static inline ExtraICState ExtractExtraICStateFromFlags(Flags flags); + static inline int ExtractArgumentsCountFromFlags(Flags flags); + static inline Flags RemoveTypeFromFlags(Flags flags); // Convert a target address into a code object. @@ -3875,34 +3875,32 @@ class Code: public HeapObject { 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. - static const int kFlagsICStateShift = 0; - static const int kFlagsICInLoopShift = 3; - static const int kFlagsTypeShift = 4; - static const int kFlagsKindShift = 8; - static const int kFlagsICHolderShift = 12; - static const int kFlagsExtraICStateShift = 13; - static const int kFlagsArgumentsCountShift = 15; - - static const int kFlagsICStateMask = 0x00000007; // 00000000111 - static const int kFlagsICInLoopMask = 0x00000008; // 00000001000 - static const int kFlagsTypeMask = 0x000000F0; // 00001110000 - static const int kFlagsKindMask = 0x00000F00; // 11110000000 - static const int kFlagsCacheInPrototypeMapMask = 0x00001000; - static const int kFlagsExtraICStateMask = 0x00006000; - static const int kFlagsArgumentsCountMask = 0xFFFF8000; + // Flags layout. BitField<type, shift, size>. + class ICStateField: public BitField<InlineCacheState, 0, 3> {}; + 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 kArgumentsCountShift = 14; + static const int kArgumentsCountMask = ~((1 << kArgumentsCountShift) - 1); static const int kFlagsNotUsedInLookup = - (kFlagsICInLoopMask | kFlagsTypeMask | kFlagsCacheInPrototypeMapMask); + TypeField::kMask | CacheHolderField::kMask; private: DISALLOW_IMPLICIT_CONSTRUCTORS(Code); @@ -4021,37 +4019,37 @@ class Map: public HeapObject { inline void set_is_extensible(bool value); inline bool is_extensible(); - inline void set_elements_kind(JSObject::ElementsKind elements_kind) { - ASSERT(elements_kind < JSObject::kElementsKindCount); - ASSERT(JSObject::kElementsKindCount <= (1 << kElementsKindBitCount)); + inline void set_elements_kind(ElementsKind elements_kind) { + ASSERT(elements_kind < kElementsKindCount); + ASSERT(kElementsKindCount <= (1 << kElementsKindBitCount)); set_bit_field2((bit_field2() & ~kElementsKindMask) | (elements_kind << kElementsKindShift)); ASSERT(this->elements_kind() == elements_kind); } - inline JSObject::ElementsKind elements_kind() { - return static_cast<JSObject::ElementsKind>( + inline ElementsKind elements_kind() { + return static_cast<ElementsKind>( (bit_field2() & kElementsKindMask) >> kElementsKindShift); } // Tells whether the instance has fast elements. // Equivalent to instance->GetElementsKind() == FAST_ELEMENTS. inline bool has_fast_elements() { - return elements_kind() == JSObject::FAST_ELEMENTS; + return elements_kind() == FAST_ELEMENTS; } inline bool has_fast_double_elements() { - return elements_kind() == JSObject::FAST_DOUBLE_ELEMENTS; + return elements_kind() == FAST_DOUBLE_ELEMENTS; } inline bool has_external_array_elements() { - JSObject::ElementsKind kind(elements_kind()); - return kind >= JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND && - kind <= JSObject::LAST_EXTERNAL_ARRAY_ELEMENTS_KIND; + ElementsKind kind(elements_kind()); + return kind >= FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND && + kind <= LAST_EXTERNAL_ARRAY_ELEMENTS_KIND; } inline bool has_dictionary_elements() { - return elements_kind() == JSObject::DICTIONARY_ELEMENTS; + return elements_kind() == DICTIONARY_ELEMENTS; } // Tells whether the map is attached to SharedFunctionInfo @@ -4156,9 +4154,9 @@ class Map: public HeapObject { MUST_USE_RESULT inline MaybeObject* GetSlowElementsMap(); // Returns a new map with all transitions dropped from the descriptors and the - // ElementsKind set to one of the value corresponding to array_type. - MUST_USE_RESULT MaybeObject* GetExternalArrayElementsMap( - ExternalArrayType array_type, + // ElementsKind set. + MUST_USE_RESULT MaybeObject* GetElementsTransitionMap( + ElementsKind elements_kind, bool safe_to_add_transition); // Returns the property index for name (only valid for FAST MODE). @@ -4323,7 +4321,7 @@ class Map: public HeapObject { static const int kElementsKindMask = (-1 << kElementsKindShift) & ((1 << (kElementsKindShift + kElementsKindBitCount)) - 1); static const int8_t kMaximumBitField2FastElementValue = static_cast<int8_t>( - (JSObject::FAST_ELEMENTS + 1) << Map::kElementsKindShift) - 1; + (FAST_ELEMENTS + 1) << Map::kElementsKindShift) - 1; // Bit positions for bit field 3 static const int kIsShared = 0; @@ -4737,7 +4735,7 @@ class SharedFunctionInfo: public HeapObject { 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) @@ -5004,7 +5002,7 @@ class JSFunction: public JSObject { // [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) @@ -5981,12 +5979,12 @@ class String: public HeapObject { // ROBUST_STRING_TRAVERSAL invokes behaviour that is robust This means it // handles unexpected data without causing assert failures and it does not // do any heap allocations. This is useful when printing stack traces. - SmartPointer<char> ToCString(AllowNullsFlag allow_nulls, - RobustnessFlag robustness_flag, - int offset, - int length, - int* length_output = 0); - SmartPointer<char> ToCString( + SmartArrayPointer<char> ToCString(AllowNullsFlag allow_nulls, + RobustnessFlag robustness_flag, + int offset, + int length, + int* length_output = 0); + SmartArrayPointer<char> ToCString( AllowNullsFlag allow_nulls = DISALLOW_NULLS, RobustnessFlag robustness_flag = FAST_STRING_TRAVERSAL, int* length_output = 0); @@ -5999,7 +5997,7 @@ class String: public HeapObject { // ROBUST_STRING_TRAVERSAL invokes behaviour that is robust This means it // handles unexpected data without causing assert failures and it does not // do any heap allocations. This is useful when printing stack traces. - SmartPointer<uc16> ToWideCString( + SmartArrayPointer<uc16> ToWideCString( RobustnessFlag robustness_flag = FAST_STRING_TRAVERSAL); // Tells whether the hash code has been computed. @@ -6409,7 +6407,6 @@ class ConsString: public String { // - truncating sliced string to enable otherwise unneeded parent to be GC'ed. class SlicedString: public String { public: - inline String* parent(); inline void set_parent(String* parent); inline int offset(); @@ -6722,9 +6719,6 @@ class JSProxy: public JSReceiver { // [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); @@ -6748,6 +6742,9 @@ class JSProxy: public JSReceiver { // 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() { @@ -6764,9 +6761,11 @@ class JSProxy: public JSReceiver { // 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, @@ -6777,12 +6776,41 @@ class JSProxy: public JSReceiver { }; -// 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/parser.cc b/src/parser.cc index f8c7c416..f9500c40 100644 --- a/src/parser.cc +++ b/src/parser.cc @@ -30,6 +30,7 @@ #include "api.h" #include "ast-inl.h" #include "bootstrapper.h" +#include "char-predicates-inl.h" #include "codegen.h" #include "compiler.h" #include "func-name-inferrer.h" @@ -38,6 +39,7 @@ #include "platform.h" #include "preparser.h" #include "runtime.h" +#include "scanner-character-streams.h" #include "scopeinfo.h" #include "string-stream.h" @@ -532,7 +534,7 @@ LexicalScope::LexicalScope(Parser* parser, Scope* scope, Isolate* isolate) parser->top_scope_ = scope; parser->lexical_scope_ = this; parser->with_nesting_level_ = 0; - isolate->set_ast_node_id(AstNode::kFunctionEntryId + 1); + isolate->set_ast_node_id(AstNode::kDeclarationsId + 1); } @@ -647,6 +649,11 @@ FunctionLiteral* Parser::DoParseProgram(Handle<String> source, if (ok && top_scope_->is_strict_mode()) { CheckOctalLiteral(beg_loc, scanner().location().end_pos, &ok); } + + if (ok && harmony_block_scoping_) { + CheckConflictingVarDeclarations(scope, &ok); + } + if (ok) { result = new(zone()) FunctionLiteral( isolate(), @@ -950,18 +957,17 @@ class InitializationBlockFinder : public ParserFinder { }; -// A ThisNamedPropertyAssignmentFinder finds and marks statements of the form +// A ThisNamedPropertyAssigmentFinder finds and marks statements of the form // this.x = ...;, where x is a named property. It also determines whether a // function contains only assignments of this type. -class ThisNamedPropertyAssignmentFinder : public ParserFinder { +class ThisNamedPropertyAssigmentFinder : public ParserFinder { public: - explicit ThisNamedPropertyAssignmentFinder(Isolate* isolate) + explicit ThisNamedPropertyAssigmentFinder(Isolate* isolate) : isolate_(isolate), only_simple_this_property_assignments_(true), - names_(0), - assigned_arguments_(0), - assigned_constants_(0) { - } + names_(NULL), + assigned_arguments_(NULL), + assigned_constants_(NULL) {} void Update(Scope* scope, Statement* stat) { // Bail out if function already has property assignment that are @@ -988,17 +994,19 @@ class ThisNamedPropertyAssignmentFinder : public ParserFinder { // Returns a fixed array containing three elements for each assignment of the // form this.x = y; Handle<FixedArray> GetThisPropertyAssignments() { - if (names_.is_empty()) { + if (names_ == NULL) { return isolate_->factory()->empty_fixed_array(); } - ASSERT_EQ(names_.length(), assigned_arguments_.length()); - ASSERT_EQ(names_.length(), assigned_constants_.length()); + ASSERT(names_ != NULL); + ASSERT(assigned_arguments_ != NULL); + ASSERT_EQ(names_->length(), assigned_arguments_->length()); + ASSERT_EQ(names_->length(), assigned_constants_->length()); Handle<FixedArray> assignments = - isolate_->factory()->NewFixedArray(names_.length() * 3); - for (int i = 0; i < names_.length(); ++i) { - assignments->set(i * 3, *names_[i]); - assignments->set(i * 3 + 1, Smi::FromInt(assigned_arguments_[i])); - assignments->set(i * 3 + 2, *assigned_constants_[i]); + isolate_->factory()->NewFixedArray(names_->length() * 3); + for (int i = 0; i < names_->length(); i++) { + assignments->set(i * 3, *names_->at(i)); + assignments->set(i * 3 + 1, Smi::FromInt(assigned_arguments_->at(i))); + assignments->set(i * 3 + 2, *assigned_constants_->at(i)); } return assignments; } @@ -1055,37 +1063,18 @@ class ThisNamedPropertyAssignmentFinder : public ParserFinder { AssignmentFromSomethingElse(); } - - - - // We will potentially reorder the property assignments, so they must be - // simple enough that the ordering does not matter. void AssignmentFromParameter(Handle<String> name, int index) { - EnsureInitialized(); - for (int i = 0; i < names_.length(); ++i) { - if (name->Equals(*names_[i])) { - assigned_arguments_[i] = index; - assigned_constants_[i] = isolate_->factory()->undefined_value(); - return; - } - } - names_.Add(name); - assigned_arguments_.Add(index); - assigned_constants_.Add(isolate_->factory()->undefined_value()); + EnsureAllocation(); + names_->Add(name); + assigned_arguments_->Add(index); + assigned_constants_->Add(isolate_->factory()->undefined_value()); } void AssignmentFromConstant(Handle<String> name, Handle<Object> value) { - EnsureInitialized(); - for (int i = 0; i < names_.length(); ++i) { - if (name->Equals(*names_[i])) { - assigned_arguments_[i] = -1; - assigned_constants_[i] = value; - return; - } - } - names_.Add(name); - assigned_arguments_.Add(-1); - assigned_constants_.Add(value); + EnsureAllocation(); + names_->Add(name); + assigned_arguments_->Add(-1); + assigned_constants_->Add(value); } void AssignmentFromSomethingElse() { @@ -1093,21 +1082,22 @@ class ThisNamedPropertyAssignmentFinder : public ParserFinder { only_simple_this_property_assignments_ = false; } - void EnsureInitialized() { - if (names_.capacity() == 0) { - ASSERT(assigned_arguments_.capacity() == 0); - ASSERT(assigned_constants_.capacity() == 0); - names_.Initialize(4); - assigned_arguments_.Initialize(4); - assigned_constants_.Initialize(4); + void EnsureAllocation() { + if (names_ == NULL) { + ASSERT(assigned_arguments_ == NULL); + ASSERT(assigned_constants_ == NULL); + Zone* zone = isolate_->zone(); + names_ = new(zone) ZoneStringList(4); + assigned_arguments_ = new(zone) ZoneList<int>(4); + assigned_constants_ = new(zone) ZoneObjectList(4); } } Isolate* isolate_; bool only_simple_this_property_assignments_; - ZoneStringList names_; - ZoneList<int> assigned_arguments_; - ZoneObjectList assigned_constants_; + ZoneStringList* names_; + ZoneList<int>* assigned_arguments_; + ZoneObjectList* assigned_constants_; }; @@ -1144,7 +1134,7 @@ void* Parser::ParseSourceElements(ZoneList<Statement*>* processor, ASSERT(processor != NULL); InitializationBlockFinder block_finder(top_scope_, target_stack_); - ThisNamedPropertyAssignmentFinder this_property_assignment_finder(isolate()); + ThisNamedPropertyAssigmentFinder this_property_assignment_finder(isolate()); bool directive_prologue = true; // Parsing directive prologue. while (peek() != end_token) { @@ -1360,14 +1350,32 @@ VariableProxy* Parser::Declare(Handle<String> name, // Declare the name. var = declaration_scope->DeclareLocal(name, mode); } else { - // The name was declared before; check for conflicting re-declarations. - // We have a conflict if either of the declarations is not a var. There - // is similar code in runtime.cc in the Declare functions. + // The name was declared in this scope before; check for conflicting + // re-declarations. We have a conflict if either of the declarations is + // not a var. There is similar code in runtime.cc in the Declare + // functions. The function CheckNonConflictingScope checks for conflicting + // var and let bindings from different scopes whereas this is a check for + // conflicting declarations within the same scope. This check also covers + // + // function () { let x; { var x; } } + // + // because the var declaration is hoisted to the function scope where 'x' + // is already bound. if ((mode != Variable::VAR) || (var->mode() != Variable::VAR)) { // We only have vars, consts and lets in declarations. ASSERT(var->mode() == Variable::VAR || var->mode() == Variable::CONST || var->mode() == Variable::LET); + if (harmony_block_scoping_) { + // In harmony mode we treat re-declarations as early errors. See + // ES5 16 for a definition of early errors. + SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS); + const char* elms[2] = { "Variable", *c_string }; + Vector<const char*> args(elms, 2); + ReportMessage("redeclaration", args); + *ok = false; + return NULL; + } const char* type = (var->mode() == Variable::VAR) ? "var" : (var->mode() == Variable::CONST) ? "const" : "let"; Handle<String> type_string = @@ -1396,8 +1404,10 @@ VariableProxy* Parser::Declare(Handle<String> name, // semantic issue as long as we keep the source order, but it may be // a performance issue since it may lead to repeated // Runtime::DeclareContextSlot() calls. - VariableProxy* proxy = declaration_scope->NewUnresolved(name, false); - declaration_scope->AddDeclaration(new(zone()) Declaration(proxy, mode, fun)); + VariableProxy* proxy = declaration_scope->NewUnresolved( + name, false, scanner().location().beg_pos); + declaration_scope->AddDeclaration( + new(zone()) Declaration(proxy, mode, fun, top_scope_)); // For global const variables we bind the proxy to a variable. if (mode == Variable::CONST && declaration_scope->is_global_scope()) { @@ -1551,9 +1561,6 @@ Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) { Scope* block_scope = NewScope(top_scope_, Scope::BLOCK_SCOPE, inside_with()); - body->set_block_scope(block_scope); - block_scope->DeclareLocal(isolate()->factory()->block_scope_symbol(), - Variable::VAR); if (top_scope_->is_strict_mode()) { block_scope->EnableStrictMode(); } @@ -1576,21 +1583,11 @@ Block* Parser::ParseScopedBlock(ZoneStringList* labels, bool* ok) { } } Expect(Token::RBRACE, CHECK_OK); - - // Create exit block. - Block* exit = new(zone()) Block(isolate(), NULL, 1, false); - exit->AddStatement(new(zone()) ExitContextStatement()); - - // Create a try-finally statement. - TryFinallyStatement* try_finally = - new(zone()) TryFinallyStatement(body, exit); - try_finally->set_escaping_targets(collector.targets()); top_scope_ = saved_scope; - // Create a result block. - Block* result = new(zone()) Block(isolate(), NULL, 1, false); - result->AddStatement(try_finally); - return result; + block_scope = block_scope->FinalizeBlockScope(); + body->set_block_scope(block_scope); + return body; } @@ -1839,18 +1836,21 @@ Block* Parser::ParseVariableDeclarations(VariableDeclarationContext var_context, block->AddStatement(new(zone()) ExpressionStatement(initialize)); } - // Add an assignment node to the initialization statement block if - // we still have a pending initialization value. We must distinguish - // between variables and constants: Variable initializations are simply + // Add an assignment node to the initialization statement block if we still + // have a pending initialization value. We must distinguish between + // different kinds of declarations: 'var' initializations are simply // assignments (with all the consequences if they are inside a 'with' // statement - they may change a 'with' object property). Constant // initializations always assign to the declared constant which is // always at the function scope level. This is only relevant for // dynamically looked-up variables and constants (the start context // for constant lookups is always the function context, while it is - // the top context for variables). Sigh... + // the top context for var declared variables). Sigh... + // For 'let' declared variables the initialization is in the same scope + // as the declaration. Thus dynamic lookups are unnecessary even if the + // block scope is inside a with. if (value != NULL) { - bool in_with = is_const ? false : inside_with(); + bool in_with = mode == Variable::VAR ? inside_with() : false; VariableProxy* proxy = initialization_scope->NewUnresolved(name, in_with); Assignment* assignment = @@ -1904,7 +1904,7 @@ Statement* Parser::ParseExpressionOrLabelledStatement(ZoneStringList* labels, // structured. However, these are probably changes we want to // make later anyway so we should go back and fix this then. if (ContainsLabel(labels, label) || TargetStackContainsLabel(label)) { - SmartPointer<char> c_string = label->ToCString(DISALLOW_NULLS); + SmartArrayPointer<char> c_string = label->ToCString(DISALLOW_NULLS); const char* elms[2] = { "Label", *c_string }; Vector<const char*> args(elms, 2); ReportMessage("redeclaration", args); @@ -2219,22 +2219,19 @@ TryStatement* Parser::ParseTryStatement(bool* ok) { Expect(Token::RPAREN, CHECK_OK); if (peek() == Token::LBRACE) { - // Rewrite the catch body { B } to a block: - // { { B } ExitContext; }. Target target(&this->target_stack_, &catch_collector); catch_scope = NewScope(top_scope_, Scope::CATCH_SCOPE, inside_with()); if (top_scope_->is_strict_mode()) { catch_scope->EnableStrictMode(); } - catch_variable = catch_scope->DeclareLocal(name, Variable::VAR); - catch_block = new(zone()) Block(isolate(), NULL, 2, false); + Variable::Mode mode = harmony_block_scoping_ + ? Variable::LET : Variable::VAR; + catch_variable = catch_scope->DeclareLocal(name, mode); Scope* saved_scope = top_scope_; top_scope_ = catch_scope; - Block* catch_body = ParseBlock(NULL, CHECK_OK); + catch_block = ParseBlock(NULL, CHECK_OK); top_scope_ = saved_scope; - catch_block->AddStatement(catch_body); - catch_block->AddStatement(new(zone()) ExitContextStatement()); } else { Expect(Token::LBRACE, CHECK_OK); } @@ -3013,7 +3010,7 @@ void Parser::ReportUnexpectedToken(Token::Value token) { void Parser::ReportInvalidPreparseData(Handle<String> name, bool* ok) { - SmartPointer<char> name_string = name->ToCString(DISALLOW_NULLS); + SmartArrayPointer<char> name_string = name->ToCString(DISALLOW_NULLS); const char* element[1] = { *name_string }; ReportMessage("invalid_preparser_data", Vector<const char*>(element, 1)); @@ -3757,7 +3754,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name, reserved_loc = scanner().location(); } - top_scope_->DeclareParameter(param_name); + top_scope_->DeclareParameter(param_name, + harmony_block_scoping_ + ? Variable::LET + : Variable::VAR); num_parameters++; if (num_parameters > kMaxNumFunctionParameters) { ReportMessageAt(scanner().location(), "too_many_parameters", @@ -3884,6 +3884,10 @@ FunctionLiteral* Parser::ParseFunctionLiteral(Handle<String> function_name, } } + if (harmony_block_scoping_) { + CheckConflictingVarDeclarations(scope, CHECK_OK); + } + FunctionLiteral* function_literal = new(zone()) FunctionLiteral(isolate(), function_name, @@ -4090,6 +4094,25 @@ void Parser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) { } +void Parser::CheckConflictingVarDeclarations(Scope* scope, bool* ok) { + Declaration* decl = scope->CheckConflictingVarDeclarations(); + if (decl != NULL) { + // In harmony mode we treat conflicting variable bindinds as early + // errors. See ES5 16 for a definition of early errors. + Handle<String> name = decl->proxy()->name(); + SmartArrayPointer<char> c_string = name->ToCString(DISALLOW_NULLS); + const char* elms[2] = { "Variable", *c_string }; + Vector<const char*> args(elms, 2); + int position = decl->proxy()->position(); + Scanner::Location location = position == RelocInfo::kNoPosition + ? Scanner::Location::invalid() + : Scanner::Location(position, position + 1); + ReportMessageAt(location, "redeclaration", args); + *ok = false; + } +} + + // This function reads an identifier name and determines whether or not it // is 'get' or 'set'. Handle<String> Parser::ParseIdentifierNameOrGetOrSet(bool* is_get, diff --git a/src/parser.h b/src/parser.h index 686dac85..3312f2f5 100644 --- a/src/parser.h +++ b/src/parser.h @@ -30,10 +30,9 @@ #include "allocation.h" #include "ast.h" -#include "scanner.h" -#include "scopes.h" #include "preparse-data-format.h" #include "preparse-data.h" +#include "scopes.h" namespace v8 { namespace internal { @@ -645,6 +644,17 @@ class Parser { // Strict mode octal literal validation. void CheckOctalLiteral(int beg_pos, int end_pos, bool* ok); + // For harmony block scoping mode: Check if the scope has conflicting var/let + // declarations from different scopes. It covers for example + // + // function f() { { { var x; } let x; } } + // function g() { { var x; let x; } } + // + // The var declarations are hoisted to the function scope, but originate from + // a scope where the name has also been let bound or the var declaration is + // hoisted over such a scope. + void CheckConflictingVarDeclarations(Scope* scope, bool* ok); + // Parser support VariableProxy* Declare(Handle<String> name, Variable::Mode mode, FunctionLiteral* fun, diff --git a/src/platform-cygwin.cc b/src/platform-cygwin.cc index 85a5e4f6..a72f5da4 100644 --- a/src/platform-cygwin.cc +++ b/src/platform-cygwin.cc @@ -474,7 +474,6 @@ void Thread::YieldCPU() { class CygwinMutex : public Mutex { public: - CygwinMutex() { pthread_mutexattr_t attrs; memset(&attrs, 0, sizeof(attrs)); diff --git a/src/platform-freebsd.cc b/src/platform-freebsd.cc index 9d9f1b79..685ec3c7 100644 --- a/src/platform-freebsd.cc +++ b/src/platform-freebsd.cc @@ -471,7 +471,6 @@ void Thread::YieldCPU() { class FreeBSDMutex : public Mutex { public: - FreeBSDMutex() { pthread_mutexattr_t attrs; int result = pthread_mutexattr_init(&attrs); diff --git a/src/platform-linux.cc b/src/platform-linux.cc index 362bf47c..b152dae9 100644 --- a/src/platform-linux.cc +++ b/src/platform-linux.cc @@ -130,13 +130,7 @@ void OS::Setup() { uint64_t OS::CpuFeaturesImpliedByPlatform() { -#if(defined(__mips_hard_float) && __mips_hard_float != 0) - // Here gcc is telling us that we are on an MIPS and gcc is assuming that we - // have FPU instructions. If gcc can assume it then so can we. - return 1u << FPU; -#else return 0; // Linux runs on anything. -#endif } diff --git a/src/platform-macos.cc b/src/platform-macos.cc index be6e1572..6be941a0 100644 --- a/src/platform-macos.cc +++ b/src/platform-macos.cc @@ -558,7 +558,6 @@ void Thread::YieldCPU() { class MacOSMutex : public Mutex { public: - MacOSMutex() { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); diff --git a/src/platform-solaris.cc b/src/platform-solaris.cc index 1e79f102..035d3944 100644 --- a/src/platform-solaris.cc +++ b/src/platform-solaris.cc @@ -460,7 +460,6 @@ void Thread::YieldCPU() { class SolarisMutex : public Mutex { public: - SolarisMutex() { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); diff --git a/src/platform-win32.cc b/src/platform-win32.cc index e5df5ff3..97788e2f 100644 --- a/src/platform-win32.cc +++ b/src/platform-win32.cc @@ -1,4 +1,4 @@ -// Copyright 2006-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: @@ -35,76 +35,8 @@ #include "platform.h" #include "vm-state-inl.h" -// Extra POSIX/ANSI routines for Win32 when when using Visual Studio C++. Please -// refer to The Open Group Base Specification for specification of the correct -// semantics for these functions. -// (http://www.opengroup.org/onlinepubs/000095399/) #ifdef _MSC_VER -namespace v8 { -namespace internal { - -// Test for finite value - usually defined in math.h -int isfinite(double x) { - return _finite(x); -} - -} // namespace v8 -} // namespace internal - -// Test for a NaN (not a number) value - usually defined in math.h -int isnan(double x) { - return _isnan(x); -} - - -// Test for infinity - usually defined in math.h -int isinf(double x) { - return (_fpclass(x) & (_FPCLASS_PINF | _FPCLASS_NINF)) != 0; -} - - -// Test if x is less than y and both nominal - usually defined in math.h -int isless(double x, double y) { - return isnan(x) || isnan(y) ? 0 : x < y; -} - - -// Test if x is greater than y and both nominal - usually defined in math.h -int isgreater(double x, double y) { - return isnan(x) || isnan(y) ? 0 : x > y; -} - - -// Classify floating point number - usually defined in math.h -int fpclassify(double x) { - // Use the MS-specific _fpclass() for classification. - int flags = _fpclass(x); - - // Determine class. We cannot use a switch statement because - // the _FPCLASS_ constants are defined as flags. - if (flags & (_FPCLASS_PN | _FPCLASS_NN)) return FP_NORMAL; - if (flags & (_FPCLASS_PZ | _FPCLASS_NZ)) return FP_ZERO; - if (flags & (_FPCLASS_PD | _FPCLASS_ND)) return FP_SUBNORMAL; - if (flags & (_FPCLASS_PINF | _FPCLASS_NINF)) return FP_INFINITE; - - // All cases should be covered by the code above. - ASSERT(flags & (_FPCLASS_SNAN | _FPCLASS_QNAN)); - return FP_NAN; -} - - -// Test sign - usually defined in math.h -int signbit(double x) { - // We need to take care of the special case of both positive - // and negative versions of zero. - if (x == 0) - return _fpclass(x) & _FPCLASS_NZ; - else - return x < 0; -} - - // Case-insensitive bounded string comparisons. Use stricmp() on Win32. Usually // defined in strings.h. int strncasecmp(const char* s1, const char* s2, int n) { @@ -1367,7 +1299,7 @@ int OS::StackWalk(Vector<OS::StackFrame> frames) { // Try to locate a symbol for this frame. DWORD64 symbol_displacement; - SmartPointer<IMAGEHLP_SYMBOL64> symbol( + SmartArrayPointer<IMAGEHLP_SYMBOL64> symbol( NewArray<IMAGEHLP_SYMBOL64>(kStackWalkMaxNameLen)); if (symbol.is_empty()) return kStackWalkError; // Out of memory. memset(*symbol, 0, sizeof(IMAGEHLP_SYMBOL64) + kStackWalkMaxNameLen); diff --git a/src/platform.h b/src/platform.h index 6b2348c8..034fe340 100644 --- a/src/platform.h +++ b/src/platform.h @@ -44,7 +44,22 @@ #ifndef V8_PLATFORM_H_ #define V8_PLATFORM_H_ -#define V8_INFINITY INFINITY +#ifdef __sun +# ifndef signbit +int signbit(double x); +# endif +#endif + +// GCC specific stuff +#ifdef __GNUC__ + +// Needed for va_list on at least MinGW and Android. +#include <stdarg.h> + +#define __GNUC_VERSION__ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) + +#endif // __GNUC__ + // Windows specific stuff. #ifdef WIN32 @@ -52,27 +67,7 @@ // Microsoft Visual C++ specific stuff. #ifdef _MSC_VER -enum { - FP_NAN, - FP_INFINITE, - FP_ZERO, - FP_SUBNORMAL, - FP_NORMAL -}; - -#undef V8_INFINITY -#define V8_INFINITY HUGE_VAL - -namespace v8 { -namespace internal { -int isfinite(double x); -} } -int isnan(double x); -int isinf(double x); -int isless(double x, double y); -int isgreater(double x, double y); -int fpclassify(double x); -int signbit(double x); +#include "win32-math.h" int strncasecmp(const char* s1, const char* s2, int n); @@ -83,36 +78,6 @@ int random(); #endif // WIN32 - -#ifdef __sun -# ifndef signbit -int signbit(double x); -# endif -#endif - - -// GCC specific stuff -#ifdef __GNUC__ - -// Needed for va_list on at least MinGW and Android. -#include <stdarg.h> - -#define __GNUC_VERSION__ (__GNUC__ * 10000 + __GNUC_MINOR__ * 100) - -// Unfortunately, the INFINITY macro cannot be used with the '-pedantic' -// warning flag and certain versions of GCC due to a bug: -// http://gcc.gnu.org/bugzilla/show_bug.cgi?id=11931 -// For now, we use the more involved template-based version from <limits>, but -// only when compiling with GCC versions affected by the bug (2.96.x - 4.0.x) -// __GNUC_PREREQ is not defined in GCC for Mac OS X, so we define our own macro -#if __GNUC_VERSION__ >= 29600 && __GNUC_VERSION__ < 40100 -#include <limits> -#undef V8_INFINITY -#define V8_INFINITY std::numeric_limits<double>::infinity() -#endif - -#endif // __GNUC__ - #include "atomicops.h" #include "platform-tls.h" #include "utils.h" diff --git a/src/preparser-api.cc b/src/preparser-api.cc index 80656d5d..899489e2 100644 --- a/src/preparser-api.cc +++ b/src/preparser-api.cc @@ -25,15 +25,19 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#ifdef _MSC_VER +#define V8_WIN32_LEAN_AND_MEAN +#include "win32-headers.h" +#endif + #include "../include/v8-preparser.h" #include "globals.h" -#include "flags.h" #include "checks.h" #include "allocation.h" #include "utils.h" #include "list.h" -#include "scanner-base.h" +#include "hashmap.h" #include "preparse-data-format.h" #include "preparse-data.h" #include "preparser.h" diff --git a/src/preparser.cc b/src/preparser.cc index 1a3dd737..47d21bac 100644 --- a/src/preparser.cc +++ b/src/preparser.cc @@ -25,22 +25,31 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#include <math.h> + #include "../include/v8stdint.h" -#include "unicode.h" -#include "globals.h" -#include "checks.h" + #include "allocation.h" -#include "utils.h" +#include "checks.h" +#include "conversions.h" +#include "conversions-inl.h" +#include "globals.h" +#include "hashmap.h" #include "list.h" - -#include "scanner-base.h" #include "preparse-data-format.h" #include "preparse-data.h" #include "preparser.h" - -#include "conversions-inl.h" +#include "unicode.h" +#include "utils.h" namespace v8 { + +#ifdef _MSC_VER +// Usually defined in math.h, but not in MSVC. +// Abstracted to work +int isfinite(double value); +#endif + namespace preparser { // Preparsing checks a JavaScript program and emits preparse-data that helps @@ -68,27 +77,22 @@ void PreParser::ReportUnexpectedToken(i::Token::Value token) { // Four of the tokens are treated specially switch (token) { case i::Token::EOS: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_eos", NULL); + return ReportMessageAt(source_location, "unexpected_eos", NULL); case i::Token::NUMBER: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_token_number", NULL); + return ReportMessageAt(source_location, "unexpected_token_number", NULL); case i::Token::STRING: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_token_string", NULL); + return ReportMessageAt(source_location, "unexpected_token_string", NULL); case i::Token::IDENTIFIER: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, + return ReportMessageAt(source_location, "unexpected_token_identifier", NULL); case i::Token::FUTURE_RESERVED_WORD: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_reserved", NULL); + return ReportMessageAt(source_location, "unexpected_reserved", NULL); case i::Token::FUTURE_STRICT_RESERVED_WORD: - return ReportMessageAt(source_location.beg_pos, source_location.end_pos, + return ReportMessageAt(source_location, "unexpected_strict_reserved", NULL); default: const char* name = i::Token::String(token); - ReportMessageAt(source_location.beg_pos, source_location.end_pos, - "unexpected_token", name); + ReportMessageAt(source_location, "unexpected_token", name); } } @@ -98,7 +102,7 @@ void PreParser::ReportUnexpectedToken(i::Token::Value token) { void PreParser::CheckOctalLiteral(int beg_pos, int end_pos, bool* ok) { i::Scanner::Location octal = scanner_->octal_position(); if (beg_pos <= octal.beg_pos && octal.end_pos <= end_pos) { - ReportMessageAt(octal.beg_pos, octal.end_pos, "strict_octal_literal", NULL); + ReportMessageAt(octal, "strict_octal_literal", NULL); scanner_->clear_octal_position(); *ok = false; } @@ -251,7 +255,7 @@ PreParser::Statement PreParser::ParseFunctionDeclaration(bool* ok) { if (identifier.IsFutureStrictReserved()) { type = "strict_reserved_word"; } - ReportMessageAt(location.beg_pos, location.end_pos, type, NULL); + ReportMessageAt(location, type, NULL); *ok = false; } return Statement::FunctionDeclaration(); @@ -313,8 +317,7 @@ PreParser::Statement PreParser::ParseVariableDeclarations( } else if (peek() == i::Token::CONST) { if (strict_mode()) { i::Scanner::Location location = scanner_->peek_location(); - ReportMessageAt(location.beg_pos, location.end_pos, - "strict_const", NULL); + ReportMessageAt(location, "strict_const", NULL); *ok = false; return Statement::Default(); } @@ -475,8 +478,7 @@ PreParser::Statement PreParser::ParseWithStatement(bool* ok) { Expect(i::Token::WITH, CHECK_OK); if (strict_mode()) { i::Scanner::Location location = scanner_->location(); - ReportMessageAt(location.beg_pos, location.end_pos, - "strict_mode_with", NULL); + ReportMessageAt(location, "strict_mode_with", NULL); *ok = false; return Statement::Default(); } @@ -612,8 +614,7 @@ PreParser::Statement PreParser::ParseThrowStatement(bool* ok) { Expect(i::Token::THROW, CHECK_OK); if (scanner_->HasAnyLineTerminatorBeforeNext()) { i::JavaScriptScanner::Location pos = scanner_->location(); - ReportMessageAt(pos.beg_pos, pos.end_pos, - "newline_after_throw", NULL); + ReportMessageAt(pos, "newline_after_throw", NULL); *ok = false; return Statement::Default(); } @@ -1025,8 +1026,7 @@ PreParser::Expression PreParser::ParsePrimaryExpression(bool* ok) { if (strict_mode()) { Next(); i::Scanner::Location location = scanner_->location(); - ReportMessageAt(location.beg_pos, location.end_pos, - "strict_reserved_word", NULL); + ReportMessageAt(location, "strict_reserved_word", NULL); *ok = false; return Expression::Default(); } @@ -1107,6 +1107,39 @@ PreParser::Expression PreParser::ParseArrayLiteral(bool* ok) { return Expression::Default(); } +void PreParser::CheckDuplicate(DuplicateFinder* finder, + i::Token::Value property, + int type, + bool* ok) { + int old_type; + if (property == i::Token::NUMBER) { + old_type = finder->AddNumber(scanner_->literal_ascii_string(), type); + } else if (scanner_->is_literal_ascii()) { + old_type = finder->AddAsciiSymbol(scanner_->literal_ascii_string(), + type); + } else { + old_type = finder->AddUC16Symbol(scanner_->literal_uc16_string(), type); + } + if (HasConflict(old_type, type)) { + if (IsDataDataConflict(old_type, type)) { + // Both are data properties. + if (!strict_mode()) return; + ReportMessageAt(scanner_->location(), + "strict_duplicate_property", NULL); + } else if (IsDataAccessorConflict(old_type, type)) { + // Both a data and an accessor property with the same name. + ReportMessageAt(scanner_->location(), + "accessor_data_property", NULL); + } else { + ASSERT(IsAccessorAccessorConflict(old_type, type)); + // Both accessors of the same type. + ReportMessageAt(scanner_->location(), + "accessor_get_set", NULL); + } + *ok = false; + } +} + PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) { // ObjectLiteral :: @@ -1116,6 +1149,7 @@ PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) { // )*[','] '}' Expect(i::Token::LBRACE, CHECK_OK); + DuplicateFinder duplicate_finder(scanner_->unicode_cache()); while (peek() != i::Token::RBRACE) { i::Token::Value next = peek(); switch (next) { @@ -1140,24 +1174,30 @@ PreParser::Expression PreParser::ParseObjectLiteral(bool* ok) { if (!is_keyword) { LogSymbol(); } + PropertyType type = is_getter ? kGetterProperty : kSetterProperty; + CheckDuplicate(&duplicate_finder, name, type, CHECK_OK); ParseFunctionLiteral(CHECK_OK); if (peek() != i::Token::RBRACE) { Expect(i::Token::COMMA, CHECK_OK); } continue; // restart the while } + CheckDuplicate(&duplicate_finder, next, kValueProperty, CHECK_OK); break; } case i::Token::STRING: Consume(next); + CheckDuplicate(&duplicate_finder, next, kValueProperty, CHECK_OK); GetStringSymbol(); break; case i::Token::NUMBER: Consume(next); + CheckDuplicate(&duplicate_finder, next, kValueProperty, CHECK_OK); break; default: if (i::Token::IsKeyword(next)) { Consume(next); + CheckDuplicate(&duplicate_finder, next, kValueProperty, CHECK_OK); } else { // Unexpected token. *ok = false; @@ -1182,9 +1222,7 @@ PreParser::Expression PreParser::ParseRegExpLiteral(bool seen_equal, bool* ok) { if (!scanner_->ScanRegExpPattern(seen_equal)) { Next(); - i::JavaScriptScanner::Location location = scanner_->location(); - ReportMessageAt(location.beg_pos, location.end_pos, - "unterminated_regexp", NULL); + ReportMessageAt(scanner_->location(), "unterminated_regexp", NULL); *ok = false; return Expression::Default(); } @@ -1193,9 +1231,7 @@ PreParser::Expression PreParser::ParseRegExpLiteral(bool seen_equal, if (!scanner_->ScanRegExpFlags()) { Next(); - i::JavaScriptScanner::Location location = scanner_->location(); - ReportMessageAt(location.beg_pos, location.end_pos, - "invalid_regexp_flags", NULL); + ReportMessageAt(scanner_->location(), "invalid_regexp_flags", NULL); *ok = false; return Expression::Default(); } @@ -1240,6 +1276,7 @@ PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) { Expect(i::Token::LPAREN, CHECK_OK); int start_position = scanner_->location().beg_pos; bool done = (peek() == i::Token::RPAREN); + DuplicateFinder duplicate_finder(scanner_->unicode_cache()); while (!done) { Identifier id = ParseIdentifier(CHECK_OK); if (!id.IsValidStrictVariable()) { @@ -1248,6 +1285,20 @@ PreParser::Expression PreParser::ParseFunctionLiteral(bool* ok) { id, CHECK_OK); } + int prev_value; + if (scanner_->is_literal_ascii()) { + prev_value = + duplicate_finder.AddAsciiSymbol(scanner_->literal_ascii_string(), 1); + } else { + prev_value = + duplicate_finder.AddUC16Symbol(scanner_->literal_uc16_string(), 1); + } + + if (prev_value != 0) { + SetStrictModeViolation(scanner_->location(), + "strict_param_dupe", + CHECK_OK); + } done = (peek() == i::Token::RPAREN); if (!done) { Expect(i::Token::COMMA, CHECK_OK); @@ -1399,13 +1450,18 @@ void PreParser::SetStrictModeViolation(i::Scanner::Location location, const char* type, bool* ok) { if (strict_mode()) { - ReportMessageAt(location.beg_pos, location.end_pos, type, NULL); + ReportMessageAt(location, type, NULL); *ok = false; return; } // Delay report in case this later turns out to be strict code // (i.e., for function names and parameters prior to a "use strict" // directive). + // It's safe to overwrite an existing violation. + // It's either from a function that turned out to be non-strict, + // or it's in the current function (and we just need to report + // one error), or it's in a unclosed nesting function that wasn't + // strict (otherwise we would already be in strict mode). strict_mode_violation_location_ = location; strict_mode_violation_type_ = type; } @@ -1417,11 +1473,9 @@ void PreParser::CheckDelayedStrictModeViolation(int beg_pos, i::Scanner::Location location = strict_mode_violation_location_; if (location.IsValid() && location.beg_pos > beg_pos && location.end_pos < end_pos) { - ReportMessageAt(location.beg_pos, location.end_pos, - strict_mode_violation_type_, NULL); + ReportMessageAt(location, strict_mode_violation_type_, NULL); *ok = false; } - strict_mode_violation_location_ = i::Scanner::Location::invalid(); } @@ -1436,7 +1490,7 @@ void PreParser::StrictModeIdentifierViolation(i::Scanner::Location location, type = "strict_reserved_word"; } if (strict_mode()) { - ReportMessageAt(location.beg_pos, location.end_pos, type, NULL); + ReportMessageAt(location, type, NULL); *ok = false; return; } @@ -1488,4 +1542,138 @@ bool PreParser::peek_any_identifier() { next == i::Token::FUTURE_RESERVED_WORD || next == i::Token::FUTURE_STRICT_RESERVED_WORD; } + + +int DuplicateFinder::AddAsciiSymbol(i::Vector<const char> key, int value) { + return AddSymbol(i::Vector<const byte>::cast(key), true, value); +} + +int DuplicateFinder::AddUC16Symbol(i::Vector<const uint16_t> key, int value) { + return AddSymbol(i::Vector<const byte>::cast(key), false, value); +} + +int DuplicateFinder::AddSymbol(i::Vector<const byte> key, + bool is_ascii, + int value) { + uint32_t hash = Hash(key, is_ascii); + byte* encoding = BackupKey(key, is_ascii); + i::HashMap::Entry* entry = map_.Lookup(encoding, hash, true); + int old_value = static_cast<int>(reinterpret_cast<intptr_t>(entry->value)); + entry->value = + reinterpret_cast<void*>(static_cast<intptr_t>(value | old_value)); + return old_value; +} + + +int DuplicateFinder::AddNumber(i::Vector<const char> key, int value) { + ASSERT(key.length() > 0); + // Quick check for already being in canonical form. + if (IsNumberCanonical(key)) { + return AddAsciiSymbol(key, value); + } + + int flags = i::ALLOW_HEX | i::ALLOW_OCTALS; + double double_value = StringToDouble(unicode_constants_, key, flags, 0.0); + int length; + const char* string; + if (!isfinite(double_value)) { + string = "Infinity"; + length = 8; // strlen("Infinity"); + } else { + string = DoubleToCString(double_value, + i::Vector<char>(number_buffer_, kBufferSize)); + length = i::StrLength(string); + } + return AddSymbol(i::Vector<const byte>(reinterpret_cast<const byte*>(string), + length), true, value); +} + + +bool DuplicateFinder::IsNumberCanonical(i::Vector<const char> number) { + // Test for a safe approximation of number literals that are already + // in canonical form: max 15 digits, no leading zeroes, except an + // integer part that is a single zero, and no trailing zeros below + // the decimal point. + int pos = 0; + int length = number.length(); + if (number.length() > 15) return false; + if (number[pos] == '0') { + pos++; + } else { + while (pos < length && + static_cast<unsigned>(number[pos] - '0') <= ('9' - '0')) pos++; + } + if (length == pos) return true; + if (number[pos] != '.') return false; + pos++; + bool invalid_last_digit = true; + while (pos < length) { + byte digit = number[pos] - '0'; + if (digit > '9' - '0') return false; + invalid_last_digit = (digit == 0); + pos++; + } + return !invalid_last_digit; +} + + +uint32_t DuplicateFinder::Hash(i::Vector<const byte> key, bool is_ascii) { + // Primitive hash function, almost identical to the one used + // for strings (except that it's seeded by the length and ASCII-ness). + int length = key.length(); + uint32_t hash = (length << 1) | (is_ascii ? 1 : 0) ; + for (int i = 0; i < length; i++) { + uint32_t c = key[i]; + hash = (hash + c) * 1025; + hash ^= (hash >> 6); + } + return hash; +} + + +bool DuplicateFinder::Match(void* first, void* second) { + // Decode lengths. + // Length + ASCII-bit is encoded as base 128, most significant heptet first, + // with a 8th bit being non-zero while there are more heptets. + // The value encodes the number of bytes following, and whether the original + // was ASCII. + byte* s1 = reinterpret_cast<byte*>(first); + byte* s2 = reinterpret_cast<byte*>(second); + uint32_t length_ascii_field = 0; + byte c1; + do { + c1 = *s1; + if (c1 != *s2) return false; + length_ascii_field = (length_ascii_field << 7) | (c1 & 0x7f); + s1++; + s2++; + } while ((c1 & 0x80) != 0); + int length = static_cast<int>(length_ascii_field >> 1); + return memcmp(s1, s2, length) == 0; +} + + +byte* DuplicateFinder::BackupKey(i::Vector<const byte> bytes, + bool is_ascii) { + uint32_t ascii_length = (bytes.length() << 1) | (is_ascii ? 1 : 0); + backing_store_.StartSequence(); + // Emit ascii_length as base-128 encoded number, with the 7th bit set + // on the byte of every heptet except the last, least significant, one. + if (ascii_length >= (1 << 7)) { + if (ascii_length >= (1 << 14)) { + if (ascii_length >= (1 << 21)) { + if (ascii_length >= (1 << 28)) { + backing_store_.Add(static_cast<byte>((ascii_length >> 28) | 0x80)); + } + backing_store_.Add(static_cast<byte>((ascii_length >> 21) | 0x80u)); + } + backing_store_.Add(static_cast<byte>((ascii_length >> 14) | 0x80u)); + } + backing_store_.Add(static_cast<byte>((ascii_length >> 7) | 0x80u)); + } + backing_store_.Add(static_cast<byte>(ascii_length & 0x7f)); + + backing_store_.AddBlock(bytes); + return backing_store_.EndSequence().start(); +} } } // v8::preparser diff --git a/src/preparser.h b/src/preparser.h index cd0a530e..b97b7cff 100644 --- a/src/preparser.h +++ b/src/preparser.h @@ -28,9 +28,19 @@ #ifndef V8_PREPARSER_H #define V8_PREPARSER_H +#include "token.h" +#include "scanner.h" + namespace v8 { + +namespace internal { +class UnicodeCache; +} + namespace preparser { +typedef uint8_t byte; + // Preparsing checks a JavaScript program and emits preparse-data that helps // a later parsing to be faster. // See preparse-data-format.h for the data format. @@ -46,6 +56,53 @@ namespace preparser { namespace i = v8::internal; +class DuplicateFinder { + public: + explicit DuplicateFinder(i::UnicodeCache* constants) + : unicode_constants_(constants), + backing_store_(16), + map_(&Match) { } + + int AddAsciiSymbol(i::Vector<const char> key, int value); + int AddUC16Symbol(i::Vector<const uint16_t> key, int value); + // Add a a number literal by converting it (if necessary) + // to the string that ToString(ToNumber(literal)) would generate. + // and then adding that string with AddAsciiSymbol. + // This string is the actual value used as key in an object literal, + // and the one that must be different from the other keys. + int AddNumber(i::Vector<const char> key, int value); + + private: + int AddSymbol(i::Vector<const byte> key, bool is_ascii, int value); + // Backs up the key and its length in the backing store. + // The backup is stored with a base 127 encoding of the + // length (plus a bit saying whether the string is ASCII), + // followed by the bytes of the key. + byte* BackupKey(i::Vector<const byte> key, bool is_ascii); + + // Compare two encoded keys (both pointing into the backing store) + // for having the same base-127 encoded lengths and ASCII-ness, + // and then having the same 'length' bytes following. + static bool Match(void* first, void* second); + // Creates a hash from a sequence of bytes. + static uint32_t Hash(i::Vector<const byte> key, bool is_ascii); + // Checks whether a string containing a JS number is its canonical + // form. + static bool IsNumberCanonical(i::Vector<const char> key); + + // Size of buffer. Sufficient for using it to call DoubleToCString in + // from conversions.h. + static const int kBufferSize = 100; + + i::UnicodeCache* unicode_constants_; + // Backing store used to store strings used as hashmap keys. + i::SequenceCollector<unsigned char> backing_store_; + i::HashMap map_; + // Buffer used for string->number->canonical string conversions. + char number_buffer_[kBufferSize]; +}; + + class PreParser { public: enum PreParseResult { @@ -53,7 +110,7 @@ class PreParser { kPreParseSuccess }; - ~PreParser() { } + ~PreParser() {} // Pre-parse the program from the character stream; returns true on // success (even if parsing failed, the pre-parse data successfully @@ -67,6 +124,45 @@ class PreParser { } private: + // Used to detect duplicates in object literals. Each of the values + // kGetterProperty, kSetterProperty and kValueProperty represents + // a type of object literal property. When parsing a property, its + // type value is stored in the DuplicateFinder for the property name. + // Values are chosen so that having intersection bits means the there is + // an incompatibility. + // I.e., you can add a getter to a property that already has a setter, since + // kGetterProperty and kSetterProperty doesn't intersect, but not if it + // already has a getter or a value. Adding the getter to an existing + // setter will store the value (kGetterProperty | kSetterProperty), which + // is incompatible with adding any further properties. + enum PropertyType { + kNone = 0, + // Bit patterns representing different object literal property types. + kGetterProperty = 1, + kSetterProperty = 2, + kValueProperty = 7, + // Helper constants. + kValueFlag = 4 + }; + + // Checks the type of conflict based on values coming from PropertyType. + bool HasConflict(int type1, int type2) { return (type1 & type2) != 0; } + bool IsDataDataConflict(int type1, int type2) { + return ((type1 & type2) & kValueFlag) != 0; + } + bool IsDataAccessorConflict(int type1, int type2) { + return ((type1 ^ type2) & kValueFlag) != 0; + } + bool IsAccessorAccessorConflict(int type1, int type2) { + return ((type1 | type2) & kValueFlag) == 0; + } + + + void CheckDuplicate(DuplicateFinder* finder, + i::Token::Value property, + int type, + bool* ok); + // These types form an algebra over syntactic categories that is just // rich enough to let us recognize and propagate the constructs that // are either being counted in the preparser data, or is important @@ -371,6 +467,11 @@ class PreParser { // Report syntax error void ReportUnexpectedToken(i::Token::Value token); + void ReportMessageAt(i::Scanner::Location location, + const char* type, + const char* name_opt) { + log_->LogMessage(location.beg_pos, location.end_pos, type, name_opt); + } void ReportMessageAt(int start_pos, int end_pos, const char* type, diff --git a/src/prettyprinter.cc b/src/prettyprinter.cc index b0342934..663af284 100644 --- a/src/prettyprinter.cc +++ b/src/prettyprinter.cc @@ -131,11 +131,6 @@ void PrettyPrinter::VisitWithStatement(WithStatement* node) { } -void PrettyPrinter::VisitExitContextStatement(ExitContextStatement* node) { - Print("<exit context>"); -} - - void PrettyPrinter::VisitSwitchStatement(SwitchStatement* node) { PrintLabels(node->labels()); Print("switch ("); @@ -284,28 +279,6 @@ void PrettyPrinter::VisitArrayLiteral(ArrayLiteral* node) { } -void PrettyPrinter::VisitSlot(Slot* node) { - switch (node->type()) { - case Slot::PARAMETER: - Print("parameter[%d]", node->index()); - break; - case Slot::LOCAL: - Print("local[%d]", node->index()); - break; - case Slot::CONTEXT: - Print("context[%d]", node->index()); - break; - case Slot::LOOKUP: - Print("lookup["); - PrintLiteral(node->var()->name(), false); - Print("]"); - break; - default: - UNREACHABLE(); - } -} - - void PrettyPrinter::VisitVariableProxy(VariableProxy* node) { PrintLiteral(node->name(), false); } @@ -660,17 +633,14 @@ void AstPrinter::PrintLiteralWithModeIndented(const char* info, void AstPrinter::PrintLabelsIndented(const char* info, ZoneStringList* labels) { if (labels != NULL && labels->length() > 0) { - if (info == NULL) { - PrintIndented("LABELS "); - } else { - PrintIndented(info); - Print(" "); - } + PrintIndented(info == NULL ? "LABELS" : info); + Print(" "); PrintLabels(labels); + Print("\n"); } else if (info != NULL) { PrintIndented(info); + Print("\n"); } - Print("\n"); } @@ -751,7 +721,7 @@ void AstPrinter::VisitDeclaration(Declaration* node) { if (node->fun() == NULL) { // var or const declarations PrintLiteralWithModeIndented(Variable::Mode2String(node->mode()), - node->proxy()->AsVariable(), + node->proxy()->var(), node->proxy()->name()); } else { // function declarations @@ -805,11 +775,6 @@ void AstPrinter::VisitWithStatement(WithStatement* node) { } -void AstPrinter::VisitExitContextStatement(ExitContextStatement* node) { - PrintIndented("EXIT CONTEXT\n"); -} - - void AstPrinter::VisitSwitchStatement(SwitchStatement* node) { IndentedScope indent(this, "SWITCH"); PrintLabelsIndented(NULL, node->labels()); @@ -959,20 +924,27 @@ void AstPrinter::VisitArrayLiteral(ArrayLiteral* node) { } -void AstPrinter::VisitSlot(Slot* node) { - PrintIndented("SLOT "); - PrettyPrinter::VisitSlot(node); - Print("\n"); -} - - void AstPrinter::VisitVariableProxy(VariableProxy* node) { - PrintLiteralWithModeIndented("VAR PROXY", node->AsVariable(), node->name()); Variable* var = node->var(); - if (var != NULL && var->rewrite() != NULL) { - IndentedScope indent(this); - Visit(var->rewrite()); + EmbeddedVector<char, 128> buf; + int pos = OS::SNPrintF(buf, "VAR PROXY"); + switch (var->location()) { + case Variable::UNALLOCATED: + break; + case Variable::PARAMETER: + OS::SNPrintF(buf + pos, " parameter[%d]", var->index()); + break; + case Variable::LOCAL: + OS::SNPrintF(buf + pos, " local[%d]", var->index()); + break; + case Variable::CONTEXT: + OS::SNPrintF(buf + pos, " context[%d]", var->index()); + break; + case Variable::LOOKUP: + OS::SNPrintF(buf + pos, " lookup"); + break; } + PrintLiteralWithModeIndented(buf.start(), var, node->name()); } @@ -1130,7 +1102,7 @@ void JsonAstBuilder::AddAttributePrefix(const char* name) { void JsonAstBuilder::AddAttribute(const char* name, Handle<String> value) { - SmartPointer<char> value_string = value->ToCString(); + SmartArrayPointer<char> value_string = value->ToCString(); AddAttributePrefix(name); Print("\"%s\"", *value_string); } @@ -1202,11 +1174,6 @@ void JsonAstBuilder::VisitWithStatement(WithStatement* stmt) { } -void JsonAstBuilder::VisitExitContextStatement(ExitContextStatement* stmt) { - TagScope tag(this, "ExitContextStatement"); -} - - void JsonAstBuilder::VisitSwitchStatement(SwitchStatement* stmt) { TagScope tag(this, "SwitchStatement"); } @@ -1287,39 +1254,32 @@ void JsonAstBuilder::VisitConditional(Conditional* expr) { } -void JsonAstBuilder::VisitSlot(Slot* expr) { - TagScope tag(this, "Slot"); +void JsonAstBuilder::VisitVariableProxy(VariableProxy* expr) { + TagScope tag(this, "Variable"); { AttributesScope attributes(this); - switch (expr->type()) { - case Slot::PARAMETER: - AddAttribute("type", "PARAMETER"); + Variable* var = expr->var(); + AddAttribute("name", var->name()); + switch (var->location()) { + case Variable::UNALLOCATED: + AddAttribute("location", "UNALLOCATED"); break; - case Slot::LOCAL: - AddAttribute("type", "LOCAL"); + case Variable::PARAMETER: + AddAttribute("location", "PARAMETER"); + AddAttribute("index", var->index()); break; - case Slot::CONTEXT: - AddAttribute("type", "CONTEXT"); + case Variable::LOCAL: + AddAttribute("location", "LOCAL"); + AddAttribute("index", var->index()); break; - case Slot::LOOKUP: - AddAttribute("type", "LOOKUP"); + case Variable::CONTEXT: + AddAttribute("location", "CONTEXT"); + AddAttribute("index", var->index()); + break; + case Variable::LOOKUP: + AddAttribute("location", "LOOKUP"); break; } - AddAttribute("index", expr->index()); - } -} - - -void JsonAstBuilder::VisitVariableProxy(VariableProxy* expr) { - if (expr->var()->rewrite() == NULL) { - TagScope tag(this, "VariableProxy"); - { - AttributesScope attributes(this); - AddAttribute("name", expr->name()); - AddAttribute("mode", Variable::Mode2String(expr->var()->mode())); - } - } else { - Visit(expr->var()->rewrite()); } } diff --git a/src/prettyprinter.h b/src/prettyprinter.h index 080081dd..a26c48e4 100644 --- a/src/prettyprinter.h +++ b/src/prettyprinter.h @@ -52,7 +52,6 @@ class PrettyPrinter: public AstVisitor { // Print a node to stdout. static void PrintOut(AstNode* node); - virtual void VisitSlot(Slot* node); // Individual nodes #define DECLARE_VISIT(type) virtual void Visit##type(type* node); AST_NODE_LIST(DECLARE_VISIT) @@ -87,7 +86,6 @@ class AstPrinter: public PrettyPrinter { const char* PrintProgram(FunctionLiteral* program); // Individual nodes - virtual void VisitSlot(Slot* node); #define DECLARE_VISIT(type) virtual void Visit##type(type* node); AST_NODE_LIST(DECLARE_VISIT) #undef DECLARE_VISIT @@ -163,7 +161,6 @@ class JsonAstBuilder: public PrettyPrinter { void AddAttribute(const char* name, bool value); // AST node visit functions. - virtual void VisitSlot(Slot* node); #define DECLARE_VISIT(type) virtual void Visit##type(type* node); AST_NODE_LIST(DECLARE_VISIT) #undef DECLARE_VISIT diff --git a/src/profile-generator-inl.h b/src/profile-generator-inl.h index 8f4bc6c1..88d6e879 100644 --- a/src/profile-generator-inl.h +++ b/src/profile-generator-inl.h @@ -78,22 +78,6 @@ ProfileNode::ProfileNode(ProfileTree* tree, CodeEntry* entry) } -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 5a95445d..adf55ad2 100644 --- a/src/profile-generator.cc +++ b/src/profile-generator.cc @@ -492,6 +492,28 @@ const CodeMap::CodeTreeConfig::Value CodeMap::CodeTreeConfig::kNoValue = 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 @@ int CodeMap::GetSharedId(Address addr) { } +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 fbb6fab2..da1fdc33 100644 --- a/src/profile-generator.h +++ b/src/profile-generator.h @@ -238,9 +238,8 @@ class CpuProfile { 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 @@ class CodeMap { 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/property.cc b/src/property.cc index dd232093..7cc2df5a 100644 --- a/src/property.cc +++ b/src/property.cc @@ -1,4 +1,4 @@ -// Copyright 2006-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: @@ -52,8 +52,8 @@ void LookupResult::Print(FILE* out) { GetTransitionMap()->Print(out); PrintF(out, "\n"); break; - case EXTERNAL_ARRAY_TRANSITION: - PrintF(out, " -type = external array transition\n"); + case ELEMENTS_TRANSITION: + PrintF(out, " -type = elements transition\n"); PrintF(out, " -map:\n"); GetTransitionMap()->Print(out); PrintF(out, "\n"); diff --git a/src/property.h b/src/property.h index ddecc921..e7d9fc53 100644 --- a/src/property.h +++ b/src/property.h @@ -1,4 +1,4 @@ -// Copyright 2006-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: @@ -112,14 +112,14 @@ class MapTransitionDescriptor: public Descriptor { : Descriptor(key, map, attributes, MAP_TRANSITION) { } }; -class ExternalArrayTransitionDescriptor: public Descriptor { +class ElementsTransitionDescriptor: public Descriptor { public: - ExternalArrayTransitionDescriptor(String* key, - Map* map, - ExternalArrayType array_type) + ElementsTransitionDescriptor(String* key, + Map* map, + ElementsKind elements_kind) : Descriptor(key, map, PropertyDetails(NONE, - EXTERNAL_ARRAY_TRANSITION, - array_type)) { } + ELEMENTS_TRANSITION, + elements_kind)) { } }; // Marks a field name in a map so that adding the field is guaranteed @@ -281,7 +281,7 @@ class LookupResult BASE_EMBEDDED { Map* GetTransitionMap() { ASSERT(lookup_type_ == DESCRIPTOR_TYPE); ASSERT(type() == MAP_TRANSITION || type() == CONSTANT_TRANSITION || - type() == EXTERNAL_ARRAY_TRANSITION); + type() == ELEMENTS_TRANSITION); return Map::cast(GetValue()); } diff --git a/src/proxy.js b/src/proxy.js index 28391595..4e44cd4e 100644 --- a/src/proxy.js +++ b/src/proxy.js @@ -29,36 +29,6 @@ global.Proxy = new $Object(); 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 @@ $Proxy.create = function(handler, proto) { 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 @@ $Proxy.create = function(handler, proto) { // 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/regexp-macro-assembler-irregexp.h b/src/regexp-macro-assembler-irregexp.h index 75cf8bf9..262ead29 100644 --- a/src/regexp-macro-assembler-irregexp.h +++ b/src/regexp-macro-assembler-irregexp.h @@ -107,6 +107,7 @@ class RegExpMacroAssemblerIrregexp: public RegExpMacroAssembler { virtual IrregexpImplementation Implementation(); virtual Handle<HeapObject> GetCode(Handle<String> source); + private: void Expand(); // Code and bitmap emission. diff --git a/src/regexp-macro-assembler-tracer.h b/src/regexp-macro-assembler-tracer.h index 8c6cf3ab..1cf0349d 100644 --- a/src/regexp-macro-assembler-tracer.h +++ b/src/regexp-macro-assembler-tracer.h @@ -95,6 +95,7 @@ class RegExpMacroAssemblerTracer: public RegExpMacroAssembler { virtual void WriteCurrentPositionToRegister(int reg, int cp_offset); virtual void ClearRegisters(int reg_from, int reg_to); virtual void WriteStackPointerToRegister(int reg); + private: RegExpMacroAssembler* assembler_; }; diff --git a/src/regexp-stack.h b/src/regexp-stack.h index 59432067..5684239f 100644 --- a/src/regexp-stack.h +++ b/src/regexp-stack.h @@ -89,6 +89,7 @@ class RegExpStack { char* ArchiveStack(char* to); char* RestoreStack(char* from); void FreeThreadResources() { thread_local_.Free(); } + private: RegExpStack(); ~RegExpStack(); diff --git a/src/regexp.js b/src/regexp.js index a7f42d59..38d44961 100644 --- a/src/regexp.js +++ b/src/regexp.js @@ -405,7 +405,8 @@ var lastMatchInfoOverride = null; // ------------------------------------------------------------------- -function SetupRegExp() { +function SetUpRegExp() { + %CheckIsBootstrapping(); %FunctionSetInstanceClassName($RegExp, 'RegExp'); %FunctionSetPrototype($RegExp, new $Object()); %SetProperty($RegExp.prototype, 'constructor', $RegExp, DONT_ENUM); @@ -484,5 +485,4 @@ function SetupRegExp() { } } - -SetupRegExp(); +SetUpRegExp(); diff --git a/src/rewriter.cc b/src/rewriter.cc index ad6ce056..3d4c2dcc 100644 --- a/src/rewriter.cc +++ b/src/rewriter.cc @@ -208,7 +208,6 @@ void Processor::VisitWithStatement(WithStatement* node) { void Processor::VisitDeclaration(Declaration* node) {} void Processor::VisitEmptyStatement(EmptyStatement* node) {} void Processor::VisitReturnStatement(ReturnStatement* node) {} -void Processor::VisitExitContextStatement(ExitContextStatement* node) {} void Processor::VisitDebuggerStatement(DebuggerStatement* node) {} diff --git a/src/runtime-profiler.cc b/src/runtime-profiler.cc index 917f6d0d..26d88461 100644 --- a/src/runtime-profiler.cc +++ b/src/runtime-profiler.cc @@ -115,10 +115,8 @@ void RuntimeProfiler::AttemptOnStackReplacement(JSFunction* function) { } SharedFunctionInfo* shared = function->shared(); - // If the code is not optimizable or references context slots, don't try OSR. - if (!shared->code()->optimizable() || !shared->allows_lazy_compilation()) { - return; - } + // If the code is not optimizable, don't try OSR. + if (!shared->code()->optimizable()) return; // We are not prepared to do OSR for a function that already has an // allocated arguments object. The optimized code would bypass it for diff --git a/src/runtime.cc b/src/runtime.cc index 50f9ce19..813f98f6 100644 --- a/src/runtime.cc +++ b/src/runtime.cc @@ -32,6 +32,7 @@ #include "accessors.h" #include "api.h" #include "arguments.h" +#include "bootstrapper.h" #include "codegen.h" #include "compilation-cache.h" #include "compiler.h" @@ -51,7 +52,7 @@ #include "runtime-profiler.h" #include "runtime.h" #include "scopeinfo.h" -#include "smart-pointer.h" +#include "smart-array-pointer.h" #include "string-search.h" #include "stub-cache.h" #include "v8threads.h" @@ -176,7 +177,7 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, // Pixel elements cannot be created using an object literal. ASSERT(!copy->HasExternalArrayElements()); switch (copy->GetElementsKind()) { - case JSObject::FAST_ELEMENTS: { + case FAST_ELEMENTS: { FixedArray* elements = FixedArray::cast(copy->elements()); if (elements->map() == heap->fixed_cow_array_map()) { isolate->counters()->cow_arrays_created_runtime()->Increment(); @@ -200,7 +201,7 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, } break; } - case JSObject::DICTIONARY_ELEMENTS: { + case DICTIONARY_ELEMENTS: { NumberDictionary* element_dictionary = copy->element_dictionary(); int capacity = element_dictionary->Capacity(); for (int i = 0; i < capacity; i++) { @@ -219,19 +220,19 @@ MUST_USE_RESULT static MaybeObject* DeepCopyBoilerplate(Isolate* isolate, } break; } - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNIMPLEMENTED(); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: // No contained objects, nothing to do. break; } @@ -612,6 +613,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CreateJSProxy) { } +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]; @@ -619,6 +633,13 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_IsJSProxy) { } +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]); @@ -626,6 +647,20 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_GetHandler) { } +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]); @@ -1149,22 +1184,14 @@ static Failure* ThrowRedeclarationError(Isolate* isolate, RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { - ASSERT(args.length() == 4); + ASSERT(args.length() == 3); HandleScope scope(isolate); Handle<GlobalObject> global = Handle<GlobalObject>( isolate->context()->global()); Handle<Context> context = args.at<Context>(0); CONVERT_ARG_CHECKED(FixedArray, pairs, 1); - bool is_eval = args.smi_at(2) == 1; - StrictModeFlag strict_mode = static_cast<StrictModeFlag>(args.smi_at(3)); - ASSERT(strict_mode == kStrictMode || strict_mode == kNonStrictMode); - - // Compute the property attributes. According to ECMA-262, section - // 13, page 71, the property must be read-only and - // non-deletable. However, neither SpiderMonkey nor KJS creates the - // property as read-only, so we don't either. - PropertyAttributes base = is_eval ? NONE : DONT_DELETE; + CONVERT_SMI_ARG_CHECKED(flags, 2); // Traverse the name/value pairs and set the properties. int length = pairs->length(); @@ -1177,7 +1204,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { // assign to it when evaluating the assignment for "const x = // <expr>" the initial value is the hole. bool is_const_property = value->IsTheHole(); - + bool is_function_declaration = false; if (value->IsUndefined() || is_const_property) { // Lookup the property in the global object, and don't set the // value of the variable if the property is already there. @@ -1226,6 +1253,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { } } } else { + is_function_declaration = true; // Copy the function and update its context. Use it as value. Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(value); @@ -1239,10 +1267,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { LookupResult lookup; global->LocalLookup(*name, &lookup); - PropertyAttributes attributes = is_const_property - ? static_cast<PropertyAttributes>(base | READ_ONLY) - : base; - // There's a local property that we need to overwrite because // we're either declaring a function or there's an interceptor // that claims the property is absent. @@ -1257,6 +1281,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { return ThrowRedeclarationError(isolate, type, name); } + // Compute the property attributes. According to ECMA-262, section + // 13, page 71, the property must be read-only and + // non-deletable. However, neither SpiderMonkey nor KJS creates the + // property as read-only, so we don't either. + int attr = NONE; + if ((flags & kDeclareGlobalsEvalFlag) == 0) { + attr |= DONT_DELETE; + } + bool is_native = (flags & kDeclareGlobalsNativeFlag) != 0; + if (is_const_property || (is_native && is_function_declaration)) { + attr |= READ_ONLY; + } + // Safari does not allow the invocation of callback setters for // function declarations. To mimic this behavior, we do not allow // the invocation of setters for function values. This makes a @@ -1267,20 +1304,24 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DeclareGlobals) { if (value->IsJSFunction()) { // Do not change DONT_DELETE to false from true. if (lookup.IsProperty() && (lookup.type() != INTERCEPTOR)) { - attributes = static_cast<PropertyAttributes>( - attributes | (lookup.GetAttributes() & DONT_DELETE)); + attr |= lookup.GetAttributes() & DONT_DELETE; } + PropertyAttributes attributes = static_cast<PropertyAttributes>(attr); + RETURN_IF_EMPTY_HANDLE(isolate, SetLocalPropertyIgnoreAttributes(global, name, value, attributes)); } else { + StrictModeFlag strict_mode = + ((flags & kDeclareGlobalsStrictModeFlag) != 0) ? kStrictMode + : kNonStrictMode; RETURN_IF_EMPTY_HANDLE(isolate, SetProperty(global, name, value, - attributes, + static_cast<PropertyAttributes>(attr), strict_mode)); } } @@ -2202,6 +2243,11 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SetCode) { // 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); @@ -2499,7 +2545,7 @@ class ReplacementStringBuilder { class CompiledReplacement { public: CompiledReplacement() - : parts_(1), replacement_substrings_(0) {} + : parts_(1), replacement_substrings_(0), simple_hint_(false) {} void Compile(Handle<String> replacement, int capture_count, @@ -2515,6 +2561,10 @@ class CompiledReplacement { return parts_.length(); } + bool simple_hint() { + return simple_hint_; + } + private: enum PartType { SUBJECT_PREFIX = 1, @@ -2573,7 +2623,7 @@ class CompiledReplacement { }; template<typename Char> - static void ParseReplacementPattern(ZoneList<ReplacementPart>* parts, + static bool ParseReplacementPattern(ZoneList<ReplacementPart>* parts, Vector<Char> characters, int capture_count, int subject_length) { @@ -2670,14 +2720,17 @@ class CompiledReplacement { if (length > last) { if (last == 0) { parts->Add(ReplacementPart::ReplacementString()); + return true; } else { parts->Add(ReplacementPart::ReplacementSubString(last, length)); } } + return false; } ZoneList<ReplacementPart> parts_; ZoneList<Handle<String> > replacement_substrings_; + bool simple_hint_; }; @@ -2689,16 +2742,16 @@ void CompiledReplacement::Compile(Handle<String> replacement, String::FlatContent content = replacement->GetFlatContent(); ASSERT(content.IsFlat()); if (content.IsAscii()) { - ParseReplacementPattern(&parts_, - content.ToAsciiVector(), - capture_count, - subject_length); + simple_hint_ = ParseReplacementPattern(&parts_, + content.ToAsciiVector(), + capture_count, + subject_length); } else { ASSERT(content.IsTwoByte()); - ParseReplacementPattern(&parts_, - content.ToUC16Vector(), - capture_count, - subject_length); + simple_hint_ = ParseReplacementPattern(&parts_, + content.ToUC16Vector(), + capture_count, + subject_length); } } Isolate* isolate = replacement->GetIsolate(); @@ -2761,6 +2814,170 @@ void CompiledReplacement::Apply(ReplacementStringBuilder* builder, } +void FindAsciiStringIndices(Vector<const char> subject, + char pattern, + ZoneList<int>* indices, + unsigned int limit) { + ASSERT(limit > 0); + // Collect indices of pattern in subject using memchr. + // Stop after finding at most limit values. + const char* subject_start = reinterpret_cast<const char*>(subject.start()); + const char* subject_end = subject_start + subject.length(); + const char* pos = subject_start; + while (limit > 0) { + pos = reinterpret_cast<const char*>( + memchr(pos, pattern, subject_end - pos)); + if (pos == NULL) return; + indices->Add(static_cast<int>(pos - subject_start)); + pos++; + limit--; + } +} + + +template <typename SubjectChar, typename PatternChar> +void FindStringIndices(Isolate* isolate, + Vector<const SubjectChar> subject, + Vector<const PatternChar> pattern, + ZoneList<int>* indices, + unsigned int limit) { + ASSERT(limit > 0); + // Collect indices of pattern in subject. + // Stop after finding at most limit values. + int pattern_length = pattern.length(); + int index = 0; + StringSearch<PatternChar, SubjectChar> search(isolate, pattern); + while (limit > 0) { + index = search.Search(subject, index); + if (index < 0) return; + indices->Add(index); + index += pattern_length; + limit--; + } +} + + +void FindStringIndicesDispatch(Isolate* isolate, + String* subject, + String* pattern, + ZoneList<int>* indices, + unsigned int limit) { + { + AssertNoAllocation no_gc; + String::FlatContent subject_content = subject->GetFlatContent(); + String::FlatContent pattern_content = pattern->GetFlatContent(); + ASSERT(subject_content.IsFlat()); + ASSERT(pattern_content.IsFlat()); + if (subject_content.IsAscii()) { + Vector<const char> subject_vector = subject_content.ToAsciiVector(); + if (pattern_content.IsAscii()) { + Vector<const char> pattern_vector = pattern_content.ToAsciiVector(); + if (pattern_vector.length() == 1) { + FindAsciiStringIndices(subject_vector, + pattern_vector[0], + indices, + limit); + } else { + FindStringIndices(isolate, + subject_vector, + pattern_vector, + indices, + limit); + } + } else { + FindStringIndices(isolate, + subject_vector, + pattern_content.ToUC16Vector(), + indices, + limit); + } + } else { + Vector<const uc16> subject_vector = subject_content.ToUC16Vector(); + if (pattern->IsAsciiRepresentation()) { + FindStringIndices(isolate, + subject_vector, + pattern_content.ToAsciiVector(), + indices, + limit); + } else { + FindStringIndices(isolate, + subject_vector, + pattern_content.ToUC16Vector(), + indices, + limit); + } + } + } +} + + +template<typename ResultSeqString> +MUST_USE_RESULT static MaybeObject* StringReplaceStringWithString( + Isolate* isolate, + Handle<String> subject, + Handle<JSRegExp> pattern_regexp, + Handle<String> replacement) { + ASSERT(subject->IsFlat()); + ASSERT(replacement->IsFlat()); + + ZoneScope zone_space(isolate, DELETE_ON_EXIT); + ZoneList<int> indices(8); + ASSERT_EQ(JSRegExp::ATOM, pattern_regexp->TypeTag()); + String* pattern = + String::cast(pattern_regexp->DataAt(JSRegExp::kAtomPatternIndex)); + int subject_len = subject->length(); + int pattern_len = pattern->length(); + int replacement_len = replacement->length(); + + FindStringIndicesDispatch(isolate, *subject, pattern, &indices, 0xffffffff); + + int matches = indices.length(); + if (matches == 0) return *subject; + + int result_len = (replacement_len - pattern_len) * matches + subject_len; + int subject_pos = 0; + int result_pos = 0; + + Handle<ResultSeqString> result; + if (ResultSeqString::kHasAsciiEncoding) { + result = Handle<ResultSeqString>::cast( + isolate->factory()->NewRawAsciiString(result_len)); + } else { + result = Handle<ResultSeqString>::cast( + isolate->factory()->NewRawTwoByteString(result_len)); + } + + for (int i = 0; i < matches; i++) { + // Copy non-matched subject content. + if (subject_pos < indices.at(i)) { + String::WriteToFlat(*subject, + result->GetChars() + result_pos, + subject_pos, + indices.at(i)); + result_pos += indices.at(i) - subject_pos; + } + + // Replace match. + if (replacement_len > 0) { + String::WriteToFlat(*replacement, + result->GetChars() + result_pos, + 0, + replacement_len); + result_pos += replacement_len; + } + + subject_pos = indices.at(i) + pattern_len; + } + // Add remaining subject content at the end. + if (subject_pos < subject_len) { + String::WriteToFlat(*subject, + result->GetChars() + result_pos, + subject_pos, + subject_len); + } + return *result; +} + MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithString( Isolate* isolate, @@ -2800,6 +3017,20 @@ MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithString( bool is_global = regexp_handle->GetFlags().is_global(); + // Shortcut for simple non-regexp global replacements + if (is_global && + regexp->TypeTag() == JSRegExp::ATOM && + compiled_replacement.simple_hint()) { + if (subject_handle->HasOnlyAsciiChars() && + replacement_handle->HasOnlyAsciiChars()) { + return StringReplaceStringWithString<SeqAsciiString>( + isolate, subject_handle, regexp_handle, replacement_handle); + } else { + return StringReplaceStringWithString<SeqTwoByteString>( + isolate, subject_handle, regexp_handle, replacement_handle); + } + } + // Guessing the number of parts that the final result string is built // from. Global regexps can match any number of times, so we guess // conservatively. @@ -2885,6 +3116,20 @@ MUST_USE_RESULT static MaybeObject* StringReplaceRegExpWithEmptyString( Handle<String> subject_handle(subject); Handle<JSRegExp> regexp_handle(regexp); + + // Shortcut for simple non-regexp global replacements + if (regexp_handle->GetFlags().is_global() && + regexp_handle->TypeTag() == JSRegExp::ATOM) { + Handle<String> empty_string_handle(HEAP->empty_string()); + if (subject_handle->HasOnlyAsciiChars()) { + return StringReplaceStringWithString<SeqAsciiString>( + isolate, subject_handle, regexp_handle, empty_string_handle); + } else { + return StringReplaceStringWithString<SeqTwoByteString>( + isolate, subject_handle, regexp_handle, empty_string_handle); + } + } + Handle<JSArray> last_match_info_handle(last_match_info); Handle<Object> match = RegExpImpl::Exec(regexp_handle, subject_handle, @@ -4104,8 +4349,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_DefineOrRedefineDataProperty) { Handle<NumberDictionary> extended_dictionary = NumberDictionarySet(dictionary, index, obj_value, details); if (*extended_dictionary != *dictionary) { - if (js_object->GetElementsKind() == - JSObject::NON_STRICT_ARGUMENTS_ELEMENTS) { + if (js_object->GetElementsKind() == NON_STRICT_ARGUMENTS_ELEMENTS) { FixedArray::cast(js_object->elements())->set(1, *extended_dictionary); } else { js_object->set_elements(*extended_dictionary); @@ -4895,6 +5139,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_Typeof) { 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 @@ -5933,49 +6178,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringTrim) { } -void FindAsciiStringIndices(Vector<const char> subject, - char pattern, - ZoneList<int>* indices, - unsigned int limit) { - ASSERT(limit > 0); - // Collect indices of pattern in subject using memchr. - // Stop after finding at most limit values. - const char* subject_start = reinterpret_cast<const char*>(subject.start()); - const char* subject_end = subject_start + subject.length(); - const char* pos = subject_start; - while (limit > 0) { - pos = reinterpret_cast<const char*>( - memchr(pos, pattern, subject_end - pos)); - if (pos == NULL) return; - indices->Add(static_cast<int>(pos - subject_start)); - pos++; - limit--; - } -} - - -template <typename SubjectChar, typename PatternChar> -void FindStringIndices(Isolate* isolate, - Vector<const SubjectChar> subject, - Vector<const PatternChar> pattern, - ZoneList<int>* indices, - unsigned int limit) { - ASSERT(limit > 0); - // Collect indices of pattern in subject. - // Stop after finding at most limit values. - int pattern_length = pattern.length(); - int index = 0; - StringSearch<PatternChar, SubjectChar> search(isolate, pattern); - while (limit > 0) { - index = search.Search(subject, index); - if (index < 0) return; - indices->Add(index); - index += pattern_length; - limit--; - } -} - - RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) { ASSERT(args.length() == 3); HandleScope handle_scope(isolate); @@ -5987,6 +6189,19 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) { int pattern_length = pattern->length(); RUNTIME_ASSERT(pattern_length > 0); + if (limit == 0xffffffffu) { + Handle<Object> cached_answer(StringSplitCache::Lookup( + isolate->heap()->string_split_cache(), + *subject, + *pattern)); + if (*cached_answer != Smi::FromInt(0)) { + Handle<JSArray> result = + isolate->factory()->NewJSArrayWithElements( + Handle<FixedArray>::cast(cached_answer)); + return *result; + } + } + // The limit can be very large (0xffffffffu), but since the pattern // isn't empty, we can never create more parts than ~half the length // of the subject. @@ -6002,53 +6217,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) { ZoneList<int> indices(initial_capacity); if (!pattern->IsFlat()) FlattenString(pattern); - // No allocation block. - { - AssertNoAllocation no_gc; - String::FlatContent subject_content = subject->GetFlatContent(); - String::FlatContent pattern_content = pattern->GetFlatContent(); - ASSERT(subject_content.IsFlat()); - ASSERT(pattern_content.IsFlat()); - if (subject_content.IsAscii()) { - Vector<const char> subject_vector = subject_content.ToAsciiVector(); - if (pattern_content.IsAscii()) { - Vector<const char> pattern_vector = pattern_content.ToAsciiVector(); - if (pattern_vector.length() == 1) { - FindAsciiStringIndices(subject_vector, - pattern_vector[0], - &indices, - limit); - } else { - FindStringIndices(isolate, - subject_vector, - pattern_vector, - &indices, - limit); - } - } else { - FindStringIndices(isolate, - subject_vector, - pattern_content.ToUC16Vector(), - &indices, - limit); - } - } else { - Vector<const uc16> subject_vector = subject_content.ToUC16Vector(); - if (pattern->IsAsciiRepresentation()) { - FindStringIndices(isolate, - subject_vector, - pattern_content.ToAsciiVector(), - &indices, - limit); - } else { - FindStringIndices(isolate, - subject_vector, - pattern_content.ToUC16Vector(), - &indices, - limit); - } - } - } + FindStringIndicesDispatch(isolate, *subject, *pattern, &indices, limit); if (static_cast<uint32_t>(indices.length()) < limit) { indices.Add(subject_length); @@ -6080,6 +6249,16 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StringSplit) { part_start = part_end + pattern_length; } + if (limit == 0xffffffffu) { + if (result->HasFastElements()) { + StringSplitCache::Enter(isolate->heap(), + isolate->heap()->string_split_cache(), + *subject, + *pattern, + *elements); + } + } + return *result; } @@ -6662,7 +6841,12 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_SparseJoinWithSeparator) { // Find total length of join result. int string_length = 0; bool is_ascii = separator->IsAsciiRepresentation(); - int max_string_length = SeqAsciiString::kMaxLength; + int max_string_length; + if (is_ascii) { + max_string_length = SeqAsciiString::kMaxLength; + } else { + max_string_length = SeqTwoByteString::kMaxLength; + } bool overflow = false; CONVERT_NUMBER_CHECKED(int, elements_length, Int32, elements_array->length()); @@ -7640,7 +7824,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewArgumentsFast) { Handle<Map> old_map(result->map()); Handle<Map> new_map = isolate->factory()->CopyMapDropTransitions(old_map); - new_map->set_elements_kind(JSObject::NON_STRICT_ARGUMENTS_ELEMENTS); + new_map->set_elements_kind(NON_STRICT_ARGUMENTS_ELEMENTS); result->set_map(*new_map); result->set_elements(*parameter_map); @@ -7768,8 +7952,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewClosure) { } -static SmartPointer<Object**> GetNonBoundArguments(int bound_argc, - int* total_argc) { +static SmartArrayPointer<Object**> GetNonBoundArguments(int bound_argc, + int* total_argc) { // Find frame containing arguments passed to the caller. JavaScriptFrameIterator it; JavaScriptFrame* frame = it.frame(); @@ -7785,7 +7969,7 @@ static SmartPointer<Object**> GetNonBoundArguments(int bound_argc, &args_slots); *total_argc = bound_argc + args_count; - SmartPointer<Object**> param_data(NewArray<Object**>(*total_argc)); + SmartArrayPointer<Object**> param_data(NewArray<Object**>(*total_argc)); for (int i = 0; i < args_count; i++) { Handle<Object> val = args_slots[i].GetValue(); param_data[bound_argc + i] = val.location(); @@ -7797,7 +7981,7 @@ static SmartPointer<Object**> GetNonBoundArguments(int bound_argc, int args_count = frame->ComputeParametersCount(); *total_argc = bound_argc + args_count; - SmartPointer<Object**> param_data(NewArray<Object**>(*total_argc)); + SmartArrayPointer<Object**> param_data(NewArray<Object**>(*total_argc)); for (int i = 0; i < args_count; i++) { Handle<Object> val = Handle<Object>(frame->GetParameter(i)); param_data[bound_argc + i] = val.location(); @@ -7824,7 +8008,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NewObjectFromBound) { } int total_argc = 0; - SmartPointer<Object**> param_data = + SmartArrayPointer<Object**> param_data = GetNonBoundArguments(bound_argc, &total_argc); for (int i = 0; i < bound_argc; i++) { Handle<Object> val = Handle<Object>(bound_args->get(i)); @@ -7965,15 +8149,9 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyCompile) { } #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(); } @@ -7987,6 +8165,15 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_LazyRecompile) { 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() || @@ -8052,8 +8239,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) { 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 @@ -8072,8 +8257,6 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) { 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: "); @@ -8081,6 +8264,8 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_NotifyDeoptimized) { PrintF("]\n"); } function->ReplaceCode(function->shared()->code()); + } else { + Deoptimizer::DeoptimizeFunction(*function); } return isolate->heap()->undefined_value(); } @@ -8261,6 +8446,55 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_CompileForOnStackReplacement) { } +RUNTIME_FUNCTION(MaybeObject*, Runtime_CheckIsBootstrapping) { + RUNTIME_ASSERT(isolate->bootstrapper()->IsActive()); + return isolate->heap()->undefined_value(); +} + + +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); @@ -8737,7 +8971,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_StackGuard) { static void PrintString(String* str) { // not uncommon to have empty strings if (str->length() > 0) { - SmartPointer<char> s = + SmartArrayPointer<char> s = str->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); PrintF("%s", *s); } @@ -9332,7 +9566,7 @@ static uint32_t EstimateElementCount(Handle<JSArray> array) { uint32_t length = static_cast<uint32_t>(array->length()->Number()); int element_count = 0; switch (array->GetElementsKind()) { - case JSObject::FAST_ELEMENTS: { + case FAST_ELEMENTS: { // Fast elements can't have lengths that are not representable by // a 32-bit signed integer. ASSERT(static_cast<int32_t>(FixedArray::kMaxLength) >= 0); @@ -9343,7 +9577,7 @@ static uint32_t EstimateElementCount(Handle<JSArray> array) { } break; } - case JSObject::DICTIONARY_ELEMENTS: { + case DICTIONARY_ELEMENTS: { Handle<NumberDictionary> dictionary( NumberDictionary::cast(array->elements())); int capacity = dictionary->Capacity(); @@ -9419,9 +9653,9 @@ static int compareUInt32(const uint32_t* ap, const uint32_t* bp) { static void CollectElementIndices(Handle<JSObject> object, uint32_t range, List<uint32_t>* indices) { - JSObject::ElementsKind kind = object->GetElementsKind(); + ElementsKind kind = object->GetElementsKind(); switch (kind) { - case JSObject::FAST_ELEMENTS: { + case FAST_ELEMENTS: { Handle<FixedArray> elements(FixedArray::cast(object->elements())); uint32_t length = static_cast<uint32_t>(elements->length()); if (range < length) length = range; @@ -9432,7 +9666,7 @@ static void CollectElementIndices(Handle<JSObject> object, } break; } - case JSObject::DICTIONARY_ELEMENTS: { + case DICTIONARY_ELEMENTS: { Handle<NumberDictionary> dict(NumberDictionary::cast(object->elements())); uint32_t capacity = dict->Capacity(); for (uint32_t j = 0; j < capacity; j++) { @@ -9451,47 +9685,47 @@ static void CollectElementIndices(Handle<JSObject> object, default: { int dense_elements_length; switch (kind) { - case JSObject::EXTERNAL_PIXEL_ELEMENTS: { + case EXTERNAL_PIXEL_ELEMENTS: { dense_elements_length = ExternalPixelArray::cast(object->elements())->length(); break; } - case JSObject::EXTERNAL_BYTE_ELEMENTS: { + case EXTERNAL_BYTE_ELEMENTS: { dense_elements_length = ExternalByteArray::cast(object->elements())->length(); break; } - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { dense_elements_length = ExternalUnsignedByteArray::cast(object->elements())->length(); break; } - case JSObject::EXTERNAL_SHORT_ELEMENTS: { + case EXTERNAL_SHORT_ELEMENTS: { dense_elements_length = ExternalShortArray::cast(object->elements())->length(); break; } - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { dense_elements_length = ExternalUnsignedShortArray::cast(object->elements())->length(); break; } - case JSObject::EXTERNAL_INT_ELEMENTS: { + case EXTERNAL_INT_ELEMENTS: { dense_elements_length = ExternalIntArray::cast(object->elements())->length(); break; } - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: { + case EXTERNAL_UNSIGNED_INT_ELEMENTS: { dense_elements_length = ExternalUnsignedIntArray::cast(object->elements())->length(); break; } - case JSObject::EXTERNAL_FLOAT_ELEMENTS: { + case EXTERNAL_FLOAT_ELEMENTS: { dense_elements_length = ExternalFloatArray::cast(object->elements())->length(); break; } - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: { + case EXTERNAL_DOUBLE_ELEMENTS: { dense_elements_length = ExternalDoubleArray::cast(object->elements())->length(); break; @@ -9540,7 +9774,7 @@ static bool IterateElements(Isolate* isolate, ArrayConcatVisitor* visitor) { uint32_t length = static_cast<uint32_t>(receiver->length()->Number()); switch (receiver->GetElementsKind()) { - case JSObject::FAST_ELEMENTS: { + case FAST_ELEMENTS: { // Run through the elements FixedArray and use HasElement and GetElement // to check the prototype for missing elements. Handle<FixedArray> elements(FixedArray::cast(receiver->elements())); @@ -9561,7 +9795,7 @@ static bool IterateElements(Isolate* isolate, } break; } - case JSObject::DICTIONARY_ELEMENTS: { + case DICTIONARY_ELEMENTS: { Handle<NumberDictionary> dict(receiver->element_dictionary()); List<uint32_t> indices(dict->Capacity() / 2); // Collect all indices in the object and the prototypes less @@ -9583,7 +9817,7 @@ static bool IterateElements(Isolate* isolate, } break; } - case JSObject::EXTERNAL_PIXEL_ELEMENTS: { + case EXTERNAL_PIXEL_ELEMENTS: { Handle<ExternalPixelArray> pixels(ExternalPixelArray::cast( receiver->elements())); for (uint32_t j = 0; j < length; j++) { @@ -9592,42 +9826,42 @@ static bool IterateElements(Isolate* isolate, } break; } - case JSObject::EXTERNAL_BYTE_ELEMENTS: { + case EXTERNAL_BYTE_ELEMENTS: { IterateExternalArrayElements<ExternalByteArray, int8_t>( isolate, receiver, true, true, visitor); break; } - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: { IterateExternalArrayElements<ExternalUnsignedByteArray, uint8_t>( isolate, receiver, true, true, visitor); break; } - case JSObject::EXTERNAL_SHORT_ELEMENTS: { + case EXTERNAL_SHORT_ELEMENTS: { IterateExternalArrayElements<ExternalShortArray, int16_t>( isolate, receiver, true, true, visitor); break; } - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: { IterateExternalArrayElements<ExternalUnsignedShortArray, uint16_t>( isolate, receiver, true, true, visitor); break; } - case JSObject::EXTERNAL_INT_ELEMENTS: { + case EXTERNAL_INT_ELEMENTS: { IterateExternalArrayElements<ExternalIntArray, int32_t>( isolate, receiver, true, false, visitor); break; } - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: { + case EXTERNAL_UNSIGNED_INT_ELEMENTS: { IterateExternalArrayElements<ExternalUnsignedIntArray, uint32_t>( isolate, receiver, true, false, visitor); break; } - case JSObject::EXTERNAL_FLOAT_ELEMENTS: { + case EXTERNAL_FLOAT_ELEMENTS: { IterateExternalArrayElements<ExternalFloatArray, float>( isolate, receiver, false, false, visitor); break; } - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: { + case EXTERNAL_DOUBLE_ELEMENTS: { IterateExternalArrayElements<ExternalDoubleArray, double>( isolate, receiver, false, false, visitor); break; @@ -9999,7 +10233,7 @@ static MaybeObject* DebugLookupResultValue(Heap* heap, } case INTERCEPTOR: case MAP_TRANSITION: - case EXTERNAL_ARRAY_TRANSITION: + case ELEMENTS_TRANSITION: case CONSTANT_TRANSITION: case NULL_DESCRIPTOR: return heap->undefined_value(); @@ -10805,15 +11039,16 @@ class ScopeIterator { 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. @@ -10860,7 +11095,7 @@ class ScopeIterator { } // Return the type of the current scope. - int Type() { + ScopeType Type() { if (at_local_) { return ScopeTypeLocal; } @@ -12289,7 +12524,7 @@ RUNTIME_FUNCTION(MaybeObject*, Runtime_ExecuteInDebugContext) { // Sets a v8 flag. RUNTIME_FUNCTION(MaybeObject*, Runtime_SetFlags) { CONVERT_CHECKED(String, arg, args[0]); - SmartPointer<char> flags = + SmartArrayPointer<char> flags = arg->ToCString(DISALLOW_NULLS, ROBUST_STRING_TRAVERSAL); FlagList::SetFlagsFromString(*flags, StrLength(*flags)); return isolate->heap()->undefined_value(); diff --git a/src/runtime.h b/src/runtime.h index 91a19dfd..1538b7d8 100644 --- a/src/runtime.h +++ b/src/runtime.h @@ -79,6 +79,8 @@ namespace internal { F(PreventExtensions, 1, 1)\ \ /* Utilities */ \ + F(CheckIsBootstrapping, 0, 1) \ + F(Apply, 5, 1) \ F(GetFunctionDelegate, 1, 1) \ F(GetConstructorDelegate, 1, 1) \ F(NewArgumentsFast, 3, 1) \ @@ -286,8 +288,12 @@ namespace internal { \ /* 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 */ \ @@ -317,7 +323,7 @@ namespace internal { F(StoreContextSlot, 4, 1) \ \ /* Declarations and initialization */ \ - F(DeclareGlobals, 4, 1) \ + F(DeclareGlobals, 3, 1) \ F(DeclareContextSlot, 4, 1) \ F(InitializeVarGlobal, -1 /* 2 or 3 */, 1) \ F(InitializeConstGlobal, 2, 1) \ @@ -663,6 +669,16 @@ class Runtime : public AllStatic { static void PerformGC(Object* result); }; + +//--------------------------------------------------------------------------- +// Constants used by interface to runtime functions. + +enum kDeclareGlobalsFlags { + kDeclareGlobalsEvalFlag = 1 << 0, + kDeclareGlobalsStrictModeFlag = 1 << 1, + kDeclareGlobalsNativeFlag = 1 << 2 +}; + } } // namespace v8::internal #endif // V8_RUNTIME_H_ diff --git a/src/runtime.js b/src/runtime.js index 4b600df7..14ff1b69 100644 --- a/src/runtime.js +++ b/src/runtime.js @@ -48,6 +48,7 @@ const $Number = global.Number; const $Function = global.Function; const $Boolean = global.Boolean; const $NaN = 0/0; +const builtins = this; // ECMA-262 Section 11.9.3. function EQUALS(y) { @@ -365,7 +366,7 @@ function IN(x) { // 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]); } @@ -407,7 +408,7 @@ function CALL_NON_FUNCTION() { if (!IS_FUNCTION(delegate)) { throw %MakeTypeError('called_non_callable', [typeof this]); } - return delegate.apply(this, arguments); + return %Apply(delegate, this, arguments, 0, %_ArgumentsLength()); } @@ -416,7 +417,32 @@ function CALL_NON_FUNCTION_AS_CONSTRUCTOR() { 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); } @@ -427,7 +453,8 @@ function APPLY_PREPARE(args) { // 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; } } @@ -441,7 +468,7 @@ function APPLY_PREPARE(args) { throw %MakeRangeError('stack_overflow', []); } - if (!IS_FUNCTION(this)) { + if (!IS_SPEC_FUNCTION(this)) { throw %MakeTypeError('apply_non_function', [ %ToString(this), typeof this ]); } @@ -609,13 +636,13 @@ function IsPrimitive(x) { // 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; } @@ -627,13 +654,13 @@ function DefaultNumber(x) { // 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/safepoint-table.cc b/src/safepoint-table.cc index 28cf6e64..bcd0a1d6 100644 --- a/src/safepoint-table.cc +++ b/src/safepoint-table.cc @@ -68,8 +68,8 @@ SafepointTable::SafepointTable(Code* code) { entries_ = pc_and_deoptimization_indexes_ + (length_ * kPcAndDeoptimizationIndexSize); ASSERT(entry_size_ > 0); - ASSERT_EQ(SafepointEntry::DeoptimizationIndexField::max(), - Safepoint::kNoDeoptimizationIndex); + STATIC_ASSERT(SafepointEntry::DeoptimizationIndexField::kMax == + Safepoint::kNoDeoptimizationIndex); } diff --git a/src/scanner-base.cc b/src/scanner-base.cc deleted file mode 100644 index 62eee1a5..00000000 --- a/src/scanner-base.cc +++ /dev/null @@ -1,1090 +0,0 @@ -// 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: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Features shared by parsing and pre-parsing scanners. - -#include "../include/v8stdint.h" -#include "scanner-base.h" -#include "char-predicates-inl.h" - -namespace v8 { -namespace internal { - -// ---------------------------------------------------------------------------- -// Scanner - -Scanner::Scanner(UnicodeCache* unicode_cache) - : unicode_cache_(unicode_cache) { } - - -uc32 Scanner::ScanHexNumber(int expected_length) { - ASSERT(expected_length <= 4); // prevent overflow - - uc32 digits[4] = { 0, 0, 0, 0 }; - uc32 x = 0; - for (int i = 0; i < expected_length; i++) { - digits[i] = c0_; - int d = HexValue(c0_); - if (d < 0) { - // According to ECMA-262, 3rd, 7.8.4, page 18, these hex escapes - // should be illegal, but other JS VMs just return the - // non-escaped version of the original character. - - // Push back digits that we have advanced past. - for (int j = i-1; j >= 0; j--) { - PushBack(digits[j]); - } - return -1; - } - x = x * 16 + d; - Advance(); - } - - return x; -} - - - -// ---------------------------------------------------------------------------- -// JavaScriptScanner - -JavaScriptScanner::JavaScriptScanner(UnicodeCache* scanner_contants) - : Scanner(scanner_contants), - octal_pos_(Location::invalid()), - harmony_block_scoping_(false) { } - - -void JavaScriptScanner::Initialize(UC16CharacterStream* source) { - source_ = source; - // Need to capture identifiers in order to recognize "get" and "set" - // in object literals. - Init(); - // Skip initial whitespace allowing HTML comment ends just like - // after a newline and scan first token. - has_line_terminator_before_next_ = true; - SkipWhiteSpace(); - Scan(); -} - - -// Ensure that tokens can be stored in a byte. -STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); - -// Table of one-character tokens, by character (0x00..0x7f only). -static const byte one_char_tokens[] = { - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::LPAREN, // 0x28 - Token::RPAREN, // 0x29 - Token::ILLEGAL, - Token::ILLEGAL, - Token::COMMA, // 0x2c - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::COLON, // 0x3a - Token::SEMICOLON, // 0x3b - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::CONDITIONAL, // 0x3f - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::LBRACK, // 0x5b - Token::ILLEGAL, - Token::RBRACK, // 0x5d - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::ILLEGAL, - Token::LBRACE, // 0x7b - Token::ILLEGAL, - Token::RBRACE, // 0x7d - Token::BIT_NOT, // 0x7e - Token::ILLEGAL -}; - - -Token::Value JavaScriptScanner::Next() { - current_ = next_; - has_line_terminator_before_next_ = false; - has_multiline_comment_before_next_ = false; - if (static_cast<unsigned>(c0_) <= 0x7f) { - Token::Value token = static_cast<Token::Value>(one_char_tokens[c0_]); - if (token != Token::ILLEGAL) { - int pos = source_pos(); - next_.token = token; - next_.location.beg_pos = pos; - next_.location.end_pos = pos + 1; - Advance(); - return current_.token; - } - } - Scan(); - return current_.token; -} - - -static inline bool IsByteOrderMark(uc32 c) { - // The Unicode value U+FFFE is guaranteed never to be assigned as a - // Unicode character; this implies that in a Unicode context the - // 0xFF, 0xFE byte pattern can only be interpreted as the U+FEFF - // character expressed in little-endian byte order (since it could - // not be a U+FFFE character expressed in big-endian byte - // order). Nevertheless, we check for it to be compatible with - // Spidermonkey. - return c == 0xFEFF || c == 0xFFFE; -} - - -bool JavaScriptScanner::SkipWhiteSpace() { - int start_position = source_pos(); - - while (true) { - // We treat byte-order marks (BOMs) as whitespace for better - // compatibility with Spidermonkey and other JavaScript engines. - while (unicode_cache_->IsWhiteSpace(c0_) || IsByteOrderMark(c0_)) { - // IsWhiteSpace() includes line terminators! - if (unicode_cache_->IsLineTerminator(c0_)) { - // Ignore line terminators, but remember them. This is necessary - // for automatic semicolon insertion. - has_line_terminator_before_next_ = true; - } - Advance(); - } - - // If there is an HTML comment end '-->' at the beginning of a - // line (with only whitespace in front of it), we treat the rest - // of the line as a comment. This is in line with the way - // SpiderMonkey handles it. - if (c0_ == '-' && has_line_terminator_before_next_) { - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '>') { - // Treat the rest of the line as a comment. - SkipSingleLineComment(); - // Continue skipping white space after the comment. - continue; - } - PushBack('-'); // undo Advance() - } - PushBack('-'); // undo Advance() - } - // Return whether or not we skipped any characters. - return source_pos() != start_position; - } -} - - -Token::Value JavaScriptScanner::SkipSingleLineComment() { - Advance(); - - // The line terminator at the end of the line is not considered - // to be part of the single-line comment; it is recognized - // separately by the lexical grammar and becomes part of the - // stream of input elements for the syntactic grammar (see - // ECMA-262, section 7.4). - while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) { - Advance(); - } - - return Token::WHITESPACE; -} - - -Token::Value JavaScriptScanner::SkipMultiLineComment() { - ASSERT(c0_ == '*'); - Advance(); - - while (c0_ >= 0) { - uc32 ch = c0_; - Advance(); - if (unicode_cache_->IsLineTerminator(ch)) { - // Following ECMA-262, section 7.4, a comment containing - // a newline will make the comment count as a line-terminator. - has_multiline_comment_before_next_ = true; - } - // If we have reached the end of the multi-line comment, we - // consume the '/' and insert a whitespace. This way all - // multi-line comments are treated as whitespace. - if (ch == '*' && c0_ == '/') { - c0_ = ' '; - return Token::WHITESPACE; - } - } - - // Unterminated multi-line comment. - return Token::ILLEGAL; -} - - -Token::Value JavaScriptScanner::ScanHtmlComment() { - // Check for <!-- comments. - ASSERT(c0_ == '!'); - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '-') return SkipSingleLineComment(); - PushBack('-'); // undo Advance() - } - PushBack('!'); // undo Advance() - ASSERT(c0_ == '!'); - return Token::LT; -} - - -void JavaScriptScanner::Scan() { - next_.literal_chars = NULL; - Token::Value token; - do { - // Remember the position of the next token - next_.location.beg_pos = source_pos(); - - switch (c0_) { - case ' ': - case '\t': - Advance(); - token = Token::WHITESPACE; - break; - - case '\n': - Advance(); - has_line_terminator_before_next_ = true; - token = Token::WHITESPACE; - break; - - case '"': case '\'': - token = ScanString(); - break; - - case '<': - // < <= << <<= <!-- - Advance(); - if (c0_ == '=') { - token = Select(Token::LTE); - } else if (c0_ == '<') { - token = Select('=', Token::ASSIGN_SHL, Token::SHL); - } else if (c0_ == '!') { - token = ScanHtmlComment(); - } else { - token = Token::LT; - } - break; - - case '>': - // > >= >> >>= >>> >>>= - Advance(); - if (c0_ == '=') { - token = Select(Token::GTE); - } else if (c0_ == '>') { - // >> >>= >>> >>>= - Advance(); - if (c0_ == '=') { - token = Select(Token::ASSIGN_SAR); - } else if (c0_ == '>') { - token = Select('=', Token::ASSIGN_SHR, Token::SHR); - } else { - token = Token::SAR; - } - } else { - token = Token::GT; - } - break; - - case '=': - // = == === - Advance(); - if (c0_ == '=') { - token = Select('=', Token::EQ_STRICT, Token::EQ); - } else { - token = Token::ASSIGN; - } - break; - - case '!': - // ! != !== - Advance(); - if (c0_ == '=') { - token = Select('=', Token::NE_STRICT, Token::NE); - } else { - token = Token::NOT; - } - break; - - case '+': - // + ++ += - Advance(); - if (c0_ == '+') { - token = Select(Token::INC); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_ADD); - } else { - token = Token::ADD; - } - break; - - case '-': - // - -- --> -= - Advance(); - if (c0_ == '-') { - Advance(); - if (c0_ == '>' && has_line_terminator_before_next_) { - // For compatibility with SpiderMonkey, we skip lines that - // start with an HTML comment end '-->'. - token = SkipSingleLineComment(); - } else { - token = Token::DEC; - } - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_SUB); - } else { - token = Token::SUB; - } - break; - - case '*': - // * *= - token = Select('=', Token::ASSIGN_MUL, Token::MUL); - break; - - case '%': - // % %= - token = Select('=', Token::ASSIGN_MOD, Token::MOD); - break; - - case '/': - // / // /* /= - Advance(); - if (c0_ == '/') { - token = SkipSingleLineComment(); - } else if (c0_ == '*') { - token = SkipMultiLineComment(); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_DIV); - } else { - token = Token::DIV; - } - break; - - case '&': - // & && &= - Advance(); - if (c0_ == '&') { - token = Select(Token::AND); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_BIT_AND); - } else { - token = Token::BIT_AND; - } - break; - - case '|': - // | || |= - Advance(); - if (c0_ == '|') { - token = Select(Token::OR); - } else if (c0_ == '=') { - token = Select(Token::ASSIGN_BIT_OR); - } else { - token = Token::BIT_OR; - } - break; - - case '^': - // ^ ^= - token = Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); - break; - - case '.': - // . Number - Advance(); - if (IsDecimalDigit(c0_)) { - token = ScanNumber(true); - } else { - token = Token::PERIOD; - } - break; - - case ':': - token = Select(Token::COLON); - break; - - case ';': - token = Select(Token::SEMICOLON); - break; - - case ',': - token = Select(Token::COMMA); - break; - - case '(': - token = Select(Token::LPAREN); - break; - - case ')': - token = Select(Token::RPAREN); - break; - - case '[': - token = Select(Token::LBRACK); - break; - - case ']': - token = Select(Token::RBRACK); - break; - - case '{': - token = Select(Token::LBRACE); - break; - - case '}': - token = Select(Token::RBRACE); - break; - - case '?': - token = Select(Token::CONDITIONAL); - break; - - case '~': - token = Select(Token::BIT_NOT); - break; - - default: - if (unicode_cache_->IsIdentifierStart(c0_)) { - token = ScanIdentifierOrKeyword(); - } else if (IsDecimalDigit(c0_)) { - token = ScanNumber(false); - } else if (SkipWhiteSpace()) { - token = Token::WHITESPACE; - } else if (c0_ < 0) { - token = Token::EOS; - } else { - token = Select(Token::ILLEGAL); - } - break; - } - - // Continue scanning for tokens as long as we're just skipping - // whitespace. - } while (token == Token::WHITESPACE); - - next_.location.end_pos = source_pos(); - next_.token = token; -} - - -void JavaScriptScanner::SeekForward(int pos) { - // After this call, we will have the token at the given position as - // the "next" token. The "current" token will be invalid. - if (pos == next_.location.beg_pos) return; - int current_pos = source_pos(); - ASSERT_EQ(next_.location.end_pos, current_pos); - // Positions inside the lookahead token aren't supported. - ASSERT(pos >= current_pos); - if (pos != current_pos) { - source_->SeekForward(pos - source_->pos()); - Advance(); - // This function is only called to seek to the location - // of the end of a function (at the "}" token). It doesn't matter - // whether there was a line terminator in the part we skip. - has_line_terminator_before_next_ = false; - has_multiline_comment_before_next_ = false; - } - Scan(); -} - - -void JavaScriptScanner::ScanEscape() { - uc32 c = c0_; - Advance(); - - // Skip escaped newlines. - if (unicode_cache_->IsLineTerminator(c)) { - // Allow CR+LF newlines in multiline string literals. - if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance(); - // Allow LF+CR newlines in multiline string literals. - if (IsLineFeed(c) && IsCarriageReturn(c0_)) Advance(); - return; - } - - switch (c) { - case '\'': // fall through - case '"' : // fall through - case '\\': break; - case 'b' : c = '\b'; break; - case 'f' : c = '\f'; break; - case 'n' : c = '\n'; break; - case 'r' : c = '\r'; break; - case 't' : c = '\t'; break; - case 'u' : { - c = ScanHexNumber(4); - if (c < 0) c = 'u'; - break; - } - case 'v' : c = '\v'; break; - case 'x' : { - c = ScanHexNumber(2); - if (c < 0) c = 'x'; - break; - } - case '0' : // fall through - case '1' : // fall through - case '2' : // fall through - case '3' : // fall through - case '4' : // fall through - case '5' : // fall through - case '6' : // fall through - case '7' : c = ScanOctalEscape(c, 2); break; - } - - // According to ECMA-262, 3rd, 7.8.4 (p 18ff) these - // should be illegal, but they are commonly handled - // as non-escaped characters by JS VMs. - AddLiteralChar(c); -} - - -// Octal escapes of the forms '\0xx' and '\xxx' are not a part of -// ECMA-262. Other JS VMs support them. -uc32 JavaScriptScanner::ScanOctalEscape(uc32 c, int length) { - uc32 x = c - '0'; - int i = 0; - for (; i < length; i++) { - int d = c0_ - '0'; - if (d < 0 || d > 7) break; - int nx = x * 8 + d; - if (nx >= 256) break; - x = nx; - Advance(); - } - // Anything except '\0' is an octal escape sequence, illegal in strict mode. - // Remember the position of octal escape sequences so that an error - // can be reported later (in strict mode). - // We don't report the error immediately, because the octal escape can - // occur before the "use strict" directive. - if (c != '0' || i > 0) { - octal_pos_ = Location(source_pos() - i - 1, source_pos() - 1); - } - return x; -} - - -Token::Value JavaScriptScanner::ScanString() { - uc32 quote = c0_; - Advance(); // consume quote - - LiteralScope literal(this); - while (c0_ != quote && c0_ >= 0 - && !unicode_cache_->IsLineTerminator(c0_)) { - uc32 c = c0_; - Advance(); - if (c == '\\') { - if (c0_ < 0) return Token::ILLEGAL; - ScanEscape(); - } else { - AddLiteralChar(c); - } - } - if (c0_ != quote) return Token::ILLEGAL; - literal.Complete(); - - Advance(); // consume quote - return Token::STRING; -} - - -void JavaScriptScanner::ScanDecimalDigits() { - while (IsDecimalDigit(c0_)) - AddLiteralCharAdvance(); -} - - -Token::Value JavaScriptScanner::ScanNumber(bool seen_period) { - ASSERT(IsDecimalDigit(c0_)); // the first digit of the number or the fraction - - enum { DECIMAL, HEX, OCTAL } kind = DECIMAL; - - LiteralScope literal(this); - if (seen_period) { - // we have already seen a decimal point of the float - AddLiteralChar('.'); - ScanDecimalDigits(); // we know we have at least one digit - - } else { - // if the first character is '0' we must check for octals and hex - if (c0_ == '0') { - int start_pos = source_pos(); // For reporting octal positions. - AddLiteralCharAdvance(); - - // either 0, 0exxx, 0Exxx, 0.xxx, an octal number, or a hex number - if (c0_ == 'x' || c0_ == 'X') { - // hex number - kind = HEX; - AddLiteralCharAdvance(); - if (!IsHexDigit(c0_)) { - // we must have at least one hex digit after 'x'/'X' - return Token::ILLEGAL; - } - while (IsHexDigit(c0_)) { - AddLiteralCharAdvance(); - } - } else if ('0' <= c0_ && c0_ <= '7') { - // (possible) octal number - kind = OCTAL; - while (true) { - if (c0_ == '8' || c0_ == '9') { - kind = DECIMAL; - break; - } - if (c0_ < '0' || '7' < c0_) { - // Octal literal finished. - octal_pos_ = Location(start_pos, source_pos()); - break; - } - AddLiteralCharAdvance(); - } - } - } - - // Parse decimal digits and allow trailing fractional part. - if (kind == DECIMAL) { - ScanDecimalDigits(); // optional - if (c0_ == '.') { - AddLiteralCharAdvance(); - ScanDecimalDigits(); // optional - } - } - } - - // scan exponent, if any - if (c0_ == 'e' || c0_ == 'E') { - ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number - if (kind == OCTAL) return Token::ILLEGAL; // no exponent for octals allowed - // scan exponent - AddLiteralCharAdvance(); - if (c0_ == '+' || c0_ == '-') - AddLiteralCharAdvance(); - if (!IsDecimalDigit(c0_)) { - // we must have at least one decimal digit after 'e'/'E' - return Token::ILLEGAL; - } - ScanDecimalDigits(); - } - - // The source character immediately following a numeric literal must - // not be an identifier start or a decimal digit; see ECMA-262 - // section 7.8.3, page 17 (note that we read only one decimal digit - // if the value is 0). - if (IsDecimalDigit(c0_) || unicode_cache_->IsIdentifierStart(c0_)) - return Token::ILLEGAL; - - literal.Complete(); - - return Token::NUMBER; -} - - -uc32 JavaScriptScanner::ScanIdentifierUnicodeEscape() { - Advance(); - if (c0_ != 'u') return -1; - Advance(); - uc32 result = ScanHexNumber(4); - if (result < 0) PushBack('u'); - return result; -} - - -// ---------------------------------------------------------------------------- -// Keyword Matcher - -#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ - KEYWORD_GROUP('b') \ - KEYWORD("break", Token::BREAK) \ - KEYWORD_GROUP('c') \ - KEYWORD("case", Token::CASE) \ - KEYWORD("catch", Token::CATCH) \ - KEYWORD("class", Token::FUTURE_RESERVED_WORD) \ - KEYWORD("const", Token::CONST) \ - KEYWORD("continue", Token::CONTINUE) \ - KEYWORD_GROUP('d') \ - KEYWORD("debugger", Token::DEBUGGER) \ - KEYWORD("default", Token::DEFAULT) \ - KEYWORD("delete", Token::DELETE) \ - KEYWORD("do", Token::DO) \ - KEYWORD_GROUP('e') \ - KEYWORD("else", Token::ELSE) \ - KEYWORD("enum", Token::FUTURE_RESERVED_WORD) \ - KEYWORD("export", Token::FUTURE_RESERVED_WORD) \ - KEYWORD("extends", Token::FUTURE_RESERVED_WORD) \ - KEYWORD_GROUP('f') \ - KEYWORD("false", Token::FALSE_LITERAL) \ - KEYWORD("finally", Token::FINALLY) \ - KEYWORD("for", Token::FOR) \ - KEYWORD("function", Token::FUNCTION) \ - KEYWORD_GROUP('i') \ - KEYWORD("if", Token::IF) \ - KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("import", Token::FUTURE_RESERVED_WORD) \ - KEYWORD("in", Token::IN) \ - KEYWORD("instanceof", Token::INSTANCEOF) \ - KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD_GROUP('l') \ - KEYWORD("let", harmony_block_scoping \ - ? Token::LET : Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD_GROUP('n') \ - KEYWORD("new", Token::NEW) \ - KEYWORD("null", Token::NULL_LITERAL) \ - KEYWORD_GROUP('p') \ - KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD_GROUP('r') \ - KEYWORD("return", Token::RETURN) \ - KEYWORD_GROUP('s') \ - KEYWORD("static", Token::FUTURE_STRICT_RESERVED_WORD) \ - KEYWORD("super", Token::FUTURE_RESERVED_WORD) \ - KEYWORD("switch", Token::SWITCH) \ - KEYWORD_GROUP('t') \ - KEYWORD("this", Token::THIS) \ - KEYWORD("throw", Token::THROW) \ - KEYWORD("true", Token::TRUE_LITERAL) \ - KEYWORD("try", Token::TRY) \ - KEYWORD("typeof", Token::TYPEOF) \ - KEYWORD_GROUP('v') \ - KEYWORD("var", Token::VAR) \ - KEYWORD("void", Token::VOID) \ - KEYWORD_GROUP('w') \ - KEYWORD("while", Token::WHILE) \ - KEYWORD("with", Token::WITH) \ - KEYWORD_GROUP('y') \ - KEYWORD("yield", Token::FUTURE_STRICT_RESERVED_WORD) - - -static Token::Value KeywordOrIdentifierToken(const char* input, - int input_length, - bool harmony_block_scoping) { - ASSERT(input_length >= 1); - const int kMinLength = 2; - const int kMaxLength = 10; - if (input_length < kMinLength || input_length > kMaxLength) { - return Token::IDENTIFIER; - } - switch (input[0]) { - default: -#define KEYWORD_GROUP_CASE(ch) \ - break; \ - case ch: -#define KEYWORD(keyword, token) \ - { \ - /* 'keyword' is a char array, so sizeof(keyword) is */ \ - /* strlen(keyword) plus 1 for the NUL char. */ \ - const int keyword_length = sizeof(keyword) - 1; \ - STATIC_ASSERT(keyword_length >= kMinLength); \ - STATIC_ASSERT(keyword_length <= kMaxLength); \ - if (input_length == keyword_length && \ - input[1] == keyword[1] && \ - (keyword_length <= 2 || input[2] == keyword[2]) && \ - (keyword_length <= 3 || input[3] == keyword[3]) && \ - (keyword_length <= 4 || input[4] == keyword[4]) && \ - (keyword_length <= 5 || input[5] == keyword[5]) && \ - (keyword_length <= 6 || input[6] == keyword[6]) && \ - (keyword_length <= 7 || input[7] == keyword[7]) && \ - (keyword_length <= 8 || input[8] == keyword[8]) && \ - (keyword_length <= 9 || input[9] == keyword[9])) { \ - return token; \ - } \ - } - KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) - } - return Token::IDENTIFIER; -} - - -Token::Value JavaScriptScanner::ScanIdentifierOrKeyword() { - ASSERT(unicode_cache_->IsIdentifierStart(c0_)); - LiteralScope literal(this); - // Scan identifier start character. - if (c0_ == '\\') { - uc32 c = ScanIdentifierUnicodeEscape(); - // Only allow legal identifier start characters. - if (c < 0 || - c == '\\' || // No recursive escapes. - !unicode_cache_->IsIdentifierStart(c)) { - return Token::ILLEGAL; - } - AddLiteralChar(c); - return ScanIdentifierSuffix(&literal); - } - - uc32 first_char = c0_; - Advance(); - AddLiteralChar(first_char); - - // Scan the rest of the identifier characters. - while (unicode_cache_->IsIdentifierPart(c0_)) { - if (c0_ != '\\') { - uc32 next_char = c0_; - Advance(); - AddLiteralChar(next_char); - continue; - } - // Fallthrough if no longer able to complete keyword. - return ScanIdentifierSuffix(&literal); - } - - literal.Complete(); - - if (next_.literal_chars->is_ascii()) { - Vector<const char> chars = next_.literal_chars->ascii_literal(); - return KeywordOrIdentifierToken(chars.start(), - chars.length(), - harmony_block_scoping_); - } - - return Token::IDENTIFIER; -} - - -Token::Value JavaScriptScanner::ScanIdentifierSuffix(LiteralScope* literal) { - // Scan the rest of the identifier characters. - while (unicode_cache_->IsIdentifierPart(c0_)) { - if (c0_ == '\\') { - uc32 c = ScanIdentifierUnicodeEscape(); - // Only allow legal identifier part characters. - if (c < 0 || - c == '\\' || - !unicode_cache_->IsIdentifierPart(c)) { - return Token::ILLEGAL; - } - AddLiteralChar(c); - } else { - AddLiteralChar(c0_); - Advance(); - } - } - literal->Complete(); - - return Token::IDENTIFIER; -} - - -bool JavaScriptScanner::ScanRegExpPattern(bool seen_equal) { - // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags - bool in_character_class = false; - - // Previous token is either '/' or '/=', in the second case, the - // pattern starts at =. - next_.location.beg_pos = source_pos() - (seen_equal ? 2 : 1); - next_.location.end_pos = source_pos() - (seen_equal ? 1 : 0); - - // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5, - // the scanner should pass uninterpreted bodies to the RegExp - // constructor. - LiteralScope literal(this); - if (seen_equal) { - AddLiteralChar('='); - } - - while (c0_ != '/' || in_character_class) { - if (unicode_cache_->IsLineTerminator(c0_) || c0_ < 0) return false; - if (c0_ == '\\') { // Escape sequence. - AddLiteralCharAdvance(); - if (unicode_cache_->IsLineTerminator(c0_) || c0_ < 0) return false; - AddLiteralCharAdvance(); - // If the escape allows more characters, i.e., \x??, \u????, or \c?, - // only "safe" characters are allowed (letters, digits, underscore), - // otherwise the escape isn't valid and the invalid character has - // its normal meaning. I.e., we can just continue scanning without - // worrying whether the following characters are part of the escape - // or not, since any '/', '\\' or '[' is guaranteed to not be part - // of the escape sequence. - - // TODO(896): At some point, parse RegExps more throughly to capture - // octal esacpes in strict mode. - } else { // Unescaped character. - if (c0_ == '[') in_character_class = true; - if (c0_ == ']') in_character_class = false; - AddLiteralCharAdvance(); - } - } - Advance(); // consume '/' - - literal.Complete(); - - return true; -} - - -bool JavaScriptScanner::ScanLiteralUnicodeEscape() { - ASSERT(c0_ == '\\'); - uc32 chars_read[6] = {'\\', 'u', 0, 0, 0, 0}; - Advance(); - int i = 1; - if (c0_ == 'u') { - i++; - while (i < 6) { - Advance(); - if (!IsHexDigit(c0_)) break; - chars_read[i] = c0_; - i++; - } - } - if (i < 6) { - // Incomplete escape. Undo all advances and return false. - while (i > 0) { - i--; - PushBack(chars_read[i]); - } - return false; - } - // Complete escape. Add all chars to current literal buffer. - for (int i = 0; i < 6; i++) { - AddLiteralChar(chars_read[i]); - } - return true; -} - - -bool JavaScriptScanner::ScanRegExpFlags() { - // Scan regular expression flags. - LiteralScope literal(this); - while (unicode_cache_->IsIdentifierPart(c0_)) { - if (c0_ != '\\') { - AddLiteralCharAdvance(); - } else { - if (!ScanLiteralUnicodeEscape()) { - break; - } - } - } - literal.Complete(); - - next_.location.end_pos = source_pos() - 1; - return true; -} - -} } // namespace v8::internal diff --git a/src/scanner-base.h b/src/scanner-base.h deleted file mode 100644 index d68d240e..00000000 --- a/src/scanner-base.h +++ /dev/null @@ -1,562 +0,0 @@ -// 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: -// -// * Redistributions of source code must retain the above copyright -// notice, this list of conditions and the following disclaimer. -// * Redistributions in binary form must reproduce the above -// copyright notice, this list of conditions and the following -// disclaimer in the documentation and/or other materials provided -// with the distribution. -// * Neither the name of Google Inc. nor the names of its -// contributors may be used to endorse or promote products derived -// from this software without specific prior written permission. -// -// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -// Features shared by parsing and pre-parsing scanners. - -#ifndef V8_SCANNER_BASE_H_ -#define V8_SCANNER_BASE_H_ - -#include "allocation.h" -#include "char-predicates.h" -#include "checks.h" -#include "globals.h" -#include "token.h" -#include "unicode-inl.h" -#include "utils.h" - -namespace v8 { -namespace internal { - -// Returns the value (0 .. 15) of a hexadecimal character c. -// If c is not a legal hexadecimal character, returns a value < 0. -inline int HexValue(uc32 c) { - c -= '0'; - if (static_cast<unsigned>(c) <= 9) return c; - c = (c | 0x20) - ('a' - '0'); // detect 0x11..0x16 and 0x31..0x36. - if (static_cast<unsigned>(c) <= 5) return c + 10; - return -1; -} - - -// --------------------------------------------------------------------- -// Buffered stream of characters, using an internal UC16 buffer. - -class UC16CharacterStream { - public: - UC16CharacterStream() : pos_(0) { } - virtual ~UC16CharacterStream() { } - - // Returns and advances past the next UC16 character in the input - // stream. If there are no more characters, it returns a negative - // value. - inline uc32 Advance() { - if (buffer_cursor_ < buffer_end_ || ReadBlock()) { - pos_++; - return static_cast<uc32>(*(buffer_cursor_++)); - } - // Note: currently the following increment is necessary to avoid a - // parser problem! The scanner treats the final kEndOfInput as - // a character with a position, and does math relative to that - // position. - pos_++; - - return kEndOfInput; - } - - // Return the current position in the character stream. - // Starts at zero. - inline unsigned pos() const { return pos_; } - - // Skips forward past the next character_count UC16 characters - // in the input, or until the end of input if that comes sooner. - // Returns the number of characters actually skipped. If less - // than character_count, - inline unsigned SeekForward(unsigned character_count) { - unsigned buffered_chars = - static_cast<unsigned>(buffer_end_ - buffer_cursor_); - if (character_count <= buffered_chars) { - buffer_cursor_ += character_count; - pos_ += character_count; - return character_count; - } - return SlowSeekForward(character_count); - } - - // Pushes back the most recently read UC16 character (or negative - // value if at end of input), i.e., the value returned by the most recent - // call to Advance. - // Must not be used right after calling SeekForward. - virtual void PushBack(int32_t character) = 0; - - protected: - static const uc32 kEndOfInput = -1; - - // Ensures that the buffer_cursor_ points to the character at - // position pos_ of the input, if possible. If the position - // is at or after the end of the input, return false. If there - // are more characters available, return true. - virtual bool ReadBlock() = 0; - virtual unsigned SlowSeekForward(unsigned character_count) = 0; - - const uc16* buffer_cursor_; - const uc16* buffer_end_; - unsigned pos_; -}; - - -class UnicodeCache { -// --------------------------------------------------------------------- -// Caching predicates used by scanners. - public: - UnicodeCache() {} - typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; - - StaticResource<Utf8Decoder>* utf8_decoder() { - return &utf8_decoder_; - } - - bool IsIdentifierStart(unibrow::uchar c) { return kIsIdentifierStart.get(c); } - bool IsIdentifierPart(unibrow::uchar c) { return kIsIdentifierPart.get(c); } - bool IsLineTerminator(unibrow::uchar c) { return kIsLineTerminator.get(c); } - bool IsWhiteSpace(unibrow::uchar c) { return kIsWhiteSpace.get(c); } - - private: - - unibrow::Predicate<IdentifierStart, 128> kIsIdentifierStart; - unibrow::Predicate<IdentifierPart, 128> kIsIdentifierPart; - unibrow::Predicate<unibrow::LineTerminator, 128> kIsLineTerminator; - unibrow::Predicate<unibrow::WhiteSpace, 128> kIsWhiteSpace; - StaticResource<Utf8Decoder> utf8_decoder_; - - DISALLOW_COPY_AND_ASSIGN(UnicodeCache); -}; - - -// ---------------------------------------------------------------------------- -// LiteralBuffer - Collector of chars of literals. - -class LiteralBuffer { - public: - LiteralBuffer() : is_ascii_(true), position_(0), backing_store_() { } - - ~LiteralBuffer() { - if (backing_store_.length() > 0) { - backing_store_.Dispose(); - } - } - - inline void AddChar(uc16 character) { - if (position_ >= backing_store_.length()) ExpandBuffer(); - if (is_ascii_) { - if (character < kMaxAsciiCharCodeU) { - backing_store_[position_] = static_cast<byte>(character); - position_ += kASCIISize; - return; - } - ConvertToUC16(); - } - *reinterpret_cast<uc16*>(&backing_store_[position_]) = character; - position_ += kUC16Size; - } - - bool is_ascii() { return is_ascii_; } - - Vector<const uc16> uc16_literal() { - ASSERT(!is_ascii_); - ASSERT((position_ & 0x1) == 0); - return Vector<const uc16>( - reinterpret_cast<const uc16*>(backing_store_.start()), - position_ >> 1); - } - - Vector<const char> ascii_literal() { - ASSERT(is_ascii_); - return Vector<const char>( - reinterpret_cast<const char*>(backing_store_.start()), - position_); - } - - int length() { - return is_ascii_ ? position_ : (position_ >> 1); - } - - void Reset() { - position_ = 0; - is_ascii_ = true; - } - private: - static const int kInitialCapacity = 16; - static const int kGrowthFactory = 4; - static const int kMinConversionSlack = 256; - static const int kMaxGrowth = 1 * MB; - inline int NewCapacity(int min_capacity) { - int capacity = Max(min_capacity, backing_store_.length()); - int new_capacity = Min(capacity * kGrowthFactory, capacity + kMaxGrowth); - return new_capacity; - } - - void ExpandBuffer() { - Vector<byte> new_store = Vector<byte>::New(NewCapacity(kInitialCapacity)); - memcpy(new_store.start(), backing_store_.start(), position_); - backing_store_.Dispose(); - backing_store_ = new_store; - } - - void ConvertToUC16() { - ASSERT(is_ascii_); - Vector<byte> new_store; - int new_content_size = position_ * kUC16Size; - if (new_content_size >= backing_store_.length()) { - // Ensure room for all currently read characters as UC16 as well - // as the character about to be stored. - new_store = Vector<byte>::New(NewCapacity(new_content_size)); - } else { - new_store = backing_store_; - } - char* src = reinterpret_cast<char*>(backing_store_.start()); - uc16* dst = reinterpret_cast<uc16*>(new_store.start()); - for (int i = position_ - 1; i >= 0; i--) { - dst[i] = src[i]; - } - if (new_store.start() != backing_store_.start()) { - backing_store_.Dispose(); - backing_store_ = new_store; - } - position_ = new_content_size; - is_ascii_ = false; - } - - bool is_ascii_; - int position_; - Vector<byte> backing_store_; - - DISALLOW_COPY_AND_ASSIGN(LiteralBuffer); -}; - - -// ---------------------------------------------------------------------------- -// Scanner base-class. - -// Generic functionality used by both JSON and JavaScript scanners. -class Scanner { - public: - // -1 is outside of the range of any real source code. - static const int kNoOctalLocation = -1; - - typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; - - class LiteralScope { - public: - explicit LiteralScope(Scanner* self); - ~LiteralScope(); - void Complete(); - - private: - Scanner* scanner_; - bool complete_; - }; - - explicit Scanner(UnicodeCache* scanner_contants); - - // Returns the current token again. - Token::Value current_token() { return current_.token; } - - // One token look-ahead (past the token returned by Next()). - Token::Value peek() const { return next_.token; } - - struct Location { - Location(int b, int e) : beg_pos(b), end_pos(e) { } - Location() : beg_pos(0), end_pos(0) { } - - bool IsValid() const { - return beg_pos >= 0 && end_pos >= beg_pos; - } - - static Location invalid() { return Location(-1, -1); } - - int beg_pos; - int end_pos; - }; - - // Returns the location information for the current token - // (the token returned by Next()). - Location location() const { return current_.location; } - Location peek_location() const { return next_.location; } - - // Returns the literal string, if any, for the current token (the - // token returned by Next()). The string is 0-terminated and in - // UTF-8 format; they may contain 0-characters. Literal strings are - // collected for identifiers, strings, and numbers. - // These functions only give the correct result if the literal - // was scanned between calls to StartLiteral() and TerminateLiteral(). - bool is_literal_ascii() { - ASSERT_NOT_NULL(current_.literal_chars); - return current_.literal_chars->is_ascii(); - } - Vector<const char> literal_ascii_string() { - ASSERT_NOT_NULL(current_.literal_chars); - return current_.literal_chars->ascii_literal(); - } - Vector<const uc16> literal_uc16_string() { - ASSERT_NOT_NULL(current_.literal_chars); - return current_.literal_chars->uc16_literal(); - } - int literal_length() const { - ASSERT_NOT_NULL(current_.literal_chars); - return current_.literal_chars->length(); - } - - bool literal_contains_escapes() const { - Location location = current_.location; - int source_length = (location.end_pos - location.beg_pos); - if (current_.token == Token::STRING) { - // Subtract delimiters. - source_length -= 2; - } - return current_.literal_chars->length() != source_length; - } - - // Returns the literal string for the next token (the token that - // would be returned if Next() were called). - bool is_next_literal_ascii() { - ASSERT_NOT_NULL(next_.literal_chars); - return next_.literal_chars->is_ascii(); - } - Vector<const char> next_literal_ascii_string() { - ASSERT_NOT_NULL(next_.literal_chars); - return next_.literal_chars->ascii_literal(); - } - Vector<const uc16> next_literal_uc16_string() { - ASSERT_NOT_NULL(next_.literal_chars); - return next_.literal_chars->uc16_literal(); - } - int next_literal_length() const { - ASSERT_NOT_NULL(next_.literal_chars); - return next_.literal_chars->length(); - } - - static const int kCharacterLookaheadBufferSize = 1; - - protected: - // The current and look-ahead token. - struct TokenDesc { - Token::Value token; - Location location; - LiteralBuffer* literal_chars; - }; - - // Call this after setting source_ to the input. - void Init() { - // Set c0_ (one character ahead) - STATIC_ASSERT(kCharacterLookaheadBufferSize == 1); - Advance(); - // Initialize current_ to not refer to a literal. - current_.literal_chars = NULL; - } - - // Literal buffer support - inline void StartLiteral() { - LiteralBuffer* free_buffer = (current_.literal_chars == &literal_buffer1_) ? - &literal_buffer2_ : &literal_buffer1_; - free_buffer->Reset(); - next_.literal_chars = free_buffer; - } - - inline void AddLiteralChar(uc32 c) { - ASSERT_NOT_NULL(next_.literal_chars); - next_.literal_chars->AddChar(c); - } - - // Complete scanning of a literal. - inline void TerminateLiteral() { - // Does nothing in the current implementation. - } - - // Stops scanning of a literal and drop the collected characters, - // e.g., due to an encountered error. - inline void DropLiteral() { - next_.literal_chars = NULL; - } - - inline void AddLiteralCharAdvance() { - AddLiteralChar(c0_); - Advance(); - } - - // Low-level scanning support. - void Advance() { c0_ = source_->Advance(); } - void PushBack(uc32 ch) { - source_->PushBack(c0_); - c0_ = ch; - } - - inline Token::Value Select(Token::Value tok) { - Advance(); - return tok; - } - - inline Token::Value Select(uc32 next, Token::Value then, Token::Value else_) { - Advance(); - if (c0_ == next) { - Advance(); - return then; - } else { - return else_; - } - } - - uc32 ScanHexNumber(int expected_length); - - // Return the current source position. - int source_pos() { - return source_->pos() - kCharacterLookaheadBufferSize; - } - - UnicodeCache* unicode_cache_; - - // Buffers collecting literal strings, numbers, etc. - LiteralBuffer literal_buffer1_; - LiteralBuffer literal_buffer2_; - - TokenDesc current_; // desc for current token (as returned by Next()) - TokenDesc next_; // desc for next token (one token look-ahead) - - // Input stream. Must be initialized to an UC16CharacterStream. - UC16CharacterStream* source_; - - // One Unicode character look-ahead; c0_ < 0 at the end of the input. - uc32 c0_; -}; - -// ---------------------------------------------------------------------------- -// JavaScriptScanner - base logic for JavaScript scanning. - -class JavaScriptScanner : public Scanner { - public: - // A LiteralScope that disables recording of some types of JavaScript - // literals. If the scanner is configured to not record the specific - // type of literal, the scope will not call StartLiteral. - class LiteralScope { - public: - explicit LiteralScope(JavaScriptScanner* self) - : scanner_(self), complete_(false) { - scanner_->StartLiteral(); - } - ~LiteralScope() { - if (!complete_) scanner_->DropLiteral(); - } - void Complete() { - scanner_->TerminateLiteral(); - complete_ = true; - } - - private: - JavaScriptScanner* scanner_; - bool complete_; - }; - - explicit JavaScriptScanner(UnicodeCache* scanner_contants); - - void Initialize(UC16CharacterStream* source); - - // Returns the next token. - Token::Value Next(); - - // Returns true if there was a line terminator before the peek'ed token, - // possibly inside a multi-line comment. - bool HasAnyLineTerminatorBeforeNext() const { - return has_line_terminator_before_next_ || - has_multiline_comment_before_next_; - } - - // Scans the input as a regular expression pattern, previous - // character(s) must be /(=). Returns true if a pattern is scanned. - bool ScanRegExpPattern(bool seen_equal); - // Returns true if regexp flags are scanned (always since flags can - // be empty). - bool ScanRegExpFlags(); - - // Tells whether the buffer contains an identifier (no escapes). - // Used for checking if a property name is an identifier. - static bool IsIdentifier(unibrow::CharacterStream* buffer); - - // Scans octal escape sequence. Also accepts "\0" decimal escape sequence. - uc32 ScanOctalEscape(uc32 c, int length); - - // Returns the location of the last seen octal literal - Location octal_position() const { return octal_pos_; } - void clear_octal_position() { octal_pos_ = Location::invalid(); } - - // Seek forward to the given position. This operation does not - // work in general, for instance when there are pushed back - // characters, but works for seeking forward until simple delimiter - // tokens, which is what it is used for. - void SeekForward(int pos); - - bool HarmonyBlockScoping() const { - return harmony_block_scoping_; - } - void SetHarmonyBlockScoping(bool block_scoping) { - harmony_block_scoping_ = block_scoping; - } - - - protected: - bool SkipWhiteSpace(); - Token::Value SkipSingleLineComment(); - Token::Value SkipMultiLineComment(); - - // Scans a single JavaScript token. - void Scan(); - - void ScanDecimalDigits(); - Token::Value ScanNumber(bool seen_period); - Token::Value ScanIdentifierOrKeyword(); - Token::Value ScanIdentifierSuffix(LiteralScope* literal); - - void ScanEscape(); - Token::Value ScanString(); - - // Scans a possible HTML comment -- begins with '<!'. - Token::Value ScanHtmlComment(); - - // Decodes a unicode escape-sequence which is part of an identifier. - // If the escape sequence cannot be decoded the result is kBadChar. - uc32 ScanIdentifierUnicodeEscape(); - // Recognizes a uniocde escape-sequence and adds its characters, - // uninterpreted, to the current literal. Used for parsing RegExp - // flags. - bool ScanLiteralUnicodeEscape(); - - // Start position of the octal literal last scanned. - Location octal_pos_; - - // Whether there is a line terminator whitespace character after - // the current token, and before the next. Does not count newlines - // inside multiline comments. - bool has_line_terminator_before_next_; - // Whether there is a multi-line comment that contains a - // line-terminator after the current token, and before the next. - bool has_multiline_comment_before_next_; - // Whether we scan 'let' as a keyword for harmony block scoped - // let bindings. - bool harmony_block_scoping_; -}; - -} } // namespace v8::internal - -#endif // V8_SCANNER_BASE_H_ diff --git a/src/scanner-character-streams.cc b/src/scanner-character-streams.cc new file mode 100644 index 00000000..ee10703c --- /dev/null +++ b/src/scanner-character-streams.cc @@ -0,0 +1,307 @@ +// 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: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include "v8.h" + +#include "scanner-character-streams.h" + +#include "handles.h" +#include "unicode-inl.h" + +namespace v8 { +namespace internal { + +// ---------------------------------------------------------------------------- +// BufferedUC16CharacterStreams + +BufferedUC16CharacterStream::BufferedUC16CharacterStream() + : UC16CharacterStream(), + pushback_limit_(NULL) { + // Initialize buffer as being empty. First read will fill the buffer. + buffer_cursor_ = buffer_; + buffer_end_ = buffer_; +} + +BufferedUC16CharacterStream::~BufferedUC16CharacterStream() { } + +void BufferedUC16CharacterStream::PushBack(uc32 character) { + if (character == kEndOfInput) { + pos_--; + return; + } + if (pushback_limit_ == NULL && buffer_cursor_ > buffer_) { + // buffer_ is writable, buffer_cursor_ is const pointer. + buffer_[--buffer_cursor_ - buffer_] = static_cast<uc16>(character); + pos_--; + return; + } + SlowPushBack(static_cast<uc16>(character)); +} + + +void BufferedUC16CharacterStream::SlowPushBack(uc16 character) { + // In pushback mode, the end of the buffer contains pushback, + // and the start of the buffer (from buffer start to pushback_limit_) + // contains valid data that comes just after the pushback. + // We NULL the pushback_limit_ if pushing all the way back to the + // start of the buffer. + + if (pushback_limit_ == NULL) { + // Enter pushback mode. + pushback_limit_ = buffer_end_; + buffer_end_ = buffer_ + kBufferSize; + buffer_cursor_ = buffer_end_; + } + // Ensure that there is room for at least one pushback. + ASSERT(buffer_cursor_ > buffer_); + ASSERT(pos_ > 0); + buffer_[--buffer_cursor_ - buffer_] = character; + if (buffer_cursor_ == buffer_) { + pushback_limit_ = NULL; + } else if (buffer_cursor_ < pushback_limit_) { + pushback_limit_ = buffer_cursor_; + } + pos_--; +} + + +bool BufferedUC16CharacterStream::ReadBlock() { + buffer_cursor_ = buffer_; + if (pushback_limit_ != NULL) { + // Leave pushback mode. + buffer_end_ = pushback_limit_; + pushback_limit_ = NULL; + // If there were any valid characters left at the + // start of the buffer, use those. + if (buffer_cursor_ < buffer_end_) return true; + // Otherwise read a new block. + } + unsigned length = FillBuffer(pos_, kBufferSize); + buffer_end_ = buffer_ + length; + return length > 0; +} + + +unsigned BufferedUC16CharacterStream::SlowSeekForward(unsigned delta) { + // Leave pushback mode (i.e., ignore that there might be valid data + // in the buffer before the pushback_limit_ point). + pushback_limit_ = NULL; + return BufferSeekForward(delta); +} + +// ---------------------------------------------------------------------------- +// GenericStringUC16CharacterStream + + +GenericStringUC16CharacterStream::GenericStringUC16CharacterStream( + Handle<String> data, + unsigned start_position, + unsigned end_position) + : string_(data), + length_(end_position) { + ASSERT(end_position >= start_position); + buffer_cursor_ = buffer_; + buffer_end_ = buffer_; + pos_ = start_position; +} + + +GenericStringUC16CharacterStream::~GenericStringUC16CharacterStream() { } + + +unsigned GenericStringUC16CharacterStream::BufferSeekForward(unsigned delta) { + unsigned old_pos = pos_; + pos_ = Min(pos_ + delta, length_); + ReadBlock(); + return pos_ - old_pos; +} + + +unsigned GenericStringUC16CharacterStream::FillBuffer(unsigned from_pos, + unsigned length) { + if (from_pos >= length_) return 0; + if (from_pos + length > length_) { + length = length_ - from_pos; + } + String::WriteToFlat<uc16>(*string_, buffer_, from_pos, from_pos + length); + return length; +} + + +// ---------------------------------------------------------------------------- +// Utf8ToUC16CharacterStream +Utf8ToUC16CharacterStream::Utf8ToUC16CharacterStream(const byte* data, + unsigned length) + : BufferedUC16CharacterStream(), + raw_data_(data), + raw_data_length_(length), + raw_data_pos_(0), + raw_character_position_(0) { + ReadBlock(); +} + + +Utf8ToUC16CharacterStream::~Utf8ToUC16CharacterStream() { } + + +unsigned Utf8ToUC16CharacterStream::BufferSeekForward(unsigned delta) { + unsigned old_pos = pos_; + unsigned target_pos = pos_ + delta; + SetRawPosition(target_pos); + pos_ = raw_character_position_; + ReadBlock(); + return pos_ - old_pos; +} + + +unsigned Utf8ToUC16CharacterStream::FillBuffer(unsigned char_position, + unsigned length) { + static const unibrow::uchar kMaxUC16Character = 0xffff; + SetRawPosition(char_position); + if (raw_character_position_ != char_position) { + // char_position was not a valid position in the stream (hit the end + // while spooling to it). + return 0u; + } + unsigned i = 0; + while (i < length) { + if (raw_data_pos_ == raw_data_length_) break; + unibrow::uchar c = raw_data_[raw_data_pos_]; + if (c <= unibrow::Utf8::kMaxOneByteChar) { + raw_data_pos_++; + } else { + c = unibrow::Utf8::CalculateValue(raw_data_ + raw_data_pos_, + raw_data_length_ - raw_data_pos_, + &raw_data_pos_); + // Don't allow characters outside of the BMP. + if (c > kMaxUC16Character) { + c = unibrow::Utf8::kBadChar; + } + } + buffer_[i++] = static_cast<uc16>(c); + } + raw_character_position_ = char_position + i; + return i; +} + + +static const byte kUtf8MultiByteMask = 0xC0; +static const byte kUtf8MultiByteCharStart = 0xC0; +static const byte kUtf8MultiByteCharFollower = 0x80; + + +#ifdef DEBUG +static bool IsUtf8MultiCharacterStart(byte first_byte) { + return (first_byte & kUtf8MultiByteMask) == kUtf8MultiByteCharStart; +} +#endif + + +static bool IsUtf8MultiCharacterFollower(byte later_byte) { + return (later_byte & kUtf8MultiByteMask) == kUtf8MultiByteCharFollower; +} + + +// Move the cursor back to point at the preceding UTF-8 character start +// in the buffer. +static inline void Utf8CharacterBack(const byte* buffer, unsigned* cursor) { + byte character = buffer[--*cursor]; + if (character > unibrow::Utf8::kMaxOneByteChar) { + ASSERT(IsUtf8MultiCharacterFollower(character)); + // Last byte of a multi-byte character encoding. Step backwards until + // pointing to the first byte of the encoding, recognized by having the + // top two bits set. + while (IsUtf8MultiCharacterFollower(buffer[--*cursor])) { } + ASSERT(IsUtf8MultiCharacterStart(buffer[*cursor])); + } +} + + +// Move the cursor forward to point at the next following UTF-8 character start +// in the buffer. +static inline void Utf8CharacterForward(const byte* buffer, unsigned* cursor) { + byte character = buffer[(*cursor)++]; + if (character > unibrow::Utf8::kMaxOneByteChar) { + // First character of a multi-byte character encoding. + // The number of most-significant one-bits determines the length of the + // encoding: + // 110..... - (0xCx, 0xDx) one additional byte (minimum). + // 1110.... - (0xEx) two additional bytes. + // 11110... - (0xFx) three additional bytes (maximum). + ASSERT(IsUtf8MultiCharacterStart(character)); + // Additional bytes is: + // 1 if value in range 0xC0 .. 0xDF. + // 2 if value in range 0xE0 .. 0xEF. + // 3 if value in range 0xF0 .. 0xF7. + // Encode that in a single value. + unsigned additional_bytes = + ((0x3211u) >> (((character - 0xC0) >> 2) & 0xC)) & 0x03; + *cursor += additional_bytes; + ASSERT(!IsUtf8MultiCharacterFollower(buffer[1 + additional_bytes])); + } +} + + +void Utf8ToUC16CharacterStream::SetRawPosition(unsigned target_position) { + if (raw_character_position_ > target_position) { + // Spool backwards in utf8 buffer. + do { + Utf8CharacterBack(raw_data_, &raw_data_pos_); + raw_character_position_--; + } while (raw_character_position_ > target_position); + return; + } + // Spool forwards in the utf8 buffer. + while (raw_character_position_ < target_position) { + if (raw_data_pos_ == raw_data_length_) return; + Utf8CharacterForward(raw_data_, &raw_data_pos_); + raw_character_position_++; + } +} + + +// ---------------------------------------------------------------------------- +// ExternalTwoByteStringUC16CharacterStream + +ExternalTwoByteStringUC16CharacterStream:: + ~ExternalTwoByteStringUC16CharacterStream() { } + + +ExternalTwoByteStringUC16CharacterStream + ::ExternalTwoByteStringUC16CharacterStream( + Handle<ExternalTwoByteString> data, + int start_position, + int end_position) + : UC16CharacterStream(), + source_(data), + raw_data_(data->GetTwoByteData(start_position)) { + buffer_cursor_ = raw_data_, + buffer_end_ = raw_data_ + (end_position - start_position); + pos_ = start_position; +} + +} } // namespace v8::internal diff --git a/src/scanner-character-streams.h b/src/scanner-character-streams.h new file mode 100644 index 00000000..5c4ea2ca --- /dev/null +++ b/src/scanner-character-streams.h @@ -0,0 +1,129 @@ +// 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: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#ifndef V8_SCANNER_CHARACTER_STREAMS_H_ +#define V8_SCANNER_CHARACTER_STREAMS_H_ + +#include "scanner.h" + +namespace v8 { +namespace internal { + +// A buffered character stream based on a random access character +// source (ReadBlock can be called with pos_ pointing to any position, +// even positions before the current). +class BufferedUC16CharacterStream: public UC16CharacterStream { + public: + BufferedUC16CharacterStream(); + virtual ~BufferedUC16CharacterStream(); + + virtual void PushBack(uc32 character); + + protected: + static const unsigned kBufferSize = 512; + static const unsigned kPushBackStepSize = 16; + + virtual unsigned SlowSeekForward(unsigned delta); + virtual bool ReadBlock(); + virtual void SlowPushBack(uc16 character); + + virtual unsigned BufferSeekForward(unsigned delta) = 0; + virtual unsigned FillBuffer(unsigned position, unsigned length) = 0; + + const uc16* pushback_limit_; + uc16 buffer_[kBufferSize]; +}; + + +// Generic string stream. +class GenericStringUC16CharacterStream: public BufferedUC16CharacterStream { + public: + GenericStringUC16CharacterStream(Handle<String> data, + unsigned start_position, + unsigned end_position); + virtual ~GenericStringUC16CharacterStream(); + + protected: + virtual unsigned BufferSeekForward(unsigned delta); + virtual unsigned FillBuffer(unsigned position, unsigned length); + + Handle<String> string_; + unsigned start_position_; + unsigned length_; +}; + + +// UC16 stream based on a literal UTF-8 string. +class Utf8ToUC16CharacterStream: public BufferedUC16CharacterStream { + public: + Utf8ToUC16CharacterStream(const byte* data, unsigned length); + virtual ~Utf8ToUC16CharacterStream(); + + protected: + virtual unsigned BufferSeekForward(unsigned delta); + virtual unsigned FillBuffer(unsigned char_position, unsigned length); + void SetRawPosition(unsigned char_position); + + const byte* raw_data_; + unsigned raw_data_length_; // Measured in bytes, not characters. + unsigned raw_data_pos_; + // The character position of the character at raw_data[raw_data_pos_]. + // Not necessarily the same as pos_. + unsigned raw_character_position_; +}; + + +// UTF16 buffer to read characters from an external string. +class ExternalTwoByteStringUC16CharacterStream: public UC16CharacterStream { + public: + ExternalTwoByteStringUC16CharacterStream(Handle<ExternalTwoByteString> data, + int start_position, + int end_position); + virtual ~ExternalTwoByteStringUC16CharacterStream(); + + virtual void PushBack(uc32 character) { + ASSERT(buffer_cursor_ > raw_data_); + buffer_cursor_--; + pos_--; + } + + protected: + virtual unsigned SlowSeekForward(unsigned delta) { + // Fast case always handles seeking. + return 0; + } + virtual bool ReadBlock() { + // Entire string is read at start. + return false; + } + Handle<ExternalTwoByteString> source_; + const uc16* raw_data_; // Pointer to the actual array of characters. +}; + +} } // namespace v8::internal + +#endif // V8_SCANNER_CHARACTER_STREAMS_H_ diff --git a/src/scanner.cc b/src/scanner.cc index 5919073c..69ea8ae6 100644 --- a/src/scanner.cc +++ b/src/scanner.cc @@ -25,303 +25,1086 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#include "v8.h" +// Features shared by parsing and pre-parsing scanners. -#include "ast.h" -#include "handles.h" #include "scanner.h" -#include "unicode-inl.h" + +#include "../include/v8stdint.h" +#include "char-predicates-inl.h" namespace v8 { namespace internal { // ---------------------------------------------------------------------------- -// BufferedUC16CharacterStreams - -BufferedUC16CharacterStream::BufferedUC16CharacterStream() - : UC16CharacterStream(), - pushback_limit_(NULL) { - // Initialize buffer as being empty. First read will fill the buffer. - buffer_cursor_ = buffer_; - buffer_end_ = buffer_; +// Scanner::LiteralScope + +Scanner::LiteralScope::LiteralScope(Scanner* self) + : scanner_(self), complete_(false) { + self->StartLiteral(); } -BufferedUC16CharacterStream::~BufferedUC16CharacterStream() { } -void BufferedUC16CharacterStream::PushBack(uc32 character) { - if (character == kEndOfInput) { - pos_--; - return; - } - if (pushback_limit_ == NULL && buffer_cursor_ > buffer_) { - // buffer_ is writable, buffer_cursor_ is const pointer. - buffer_[--buffer_cursor_ - buffer_] = static_cast<uc16>(character); - pos_--; - return; - } - SlowPushBack(static_cast<uc16>(character)); +Scanner::LiteralScope::~LiteralScope() { + if (!complete_) scanner_->DropLiteral(); } -void BufferedUC16CharacterStream::SlowPushBack(uc16 character) { - // In pushback mode, the end of the buffer contains pushback, - // and the start of the buffer (from buffer start to pushback_limit_) - // contains valid data that comes just after the pushback. - // We NULL the pushback_limit_ if pushing all the way back to the - // start of the buffer. +void Scanner::LiteralScope::Complete() { + scanner_->TerminateLiteral(); + complete_ = true; +} - if (pushback_limit_ == NULL) { - // Enter pushback mode. - pushback_limit_ = buffer_end_; - buffer_end_ = buffer_ + kBufferSize; - buffer_cursor_ = buffer_end_; - } - // Ensure that there is room for at least one pushback. - ASSERT(buffer_cursor_ > buffer_); - ASSERT(pos_ > 0); - buffer_[--buffer_cursor_ - buffer_] = character; - if (buffer_cursor_ == buffer_) { - pushback_limit_ = NULL; - } else if (buffer_cursor_ < pushback_limit_) { - pushback_limit_ = buffer_cursor_; +// ---------------------------------------------------------------------------- +// Scanner + +Scanner::Scanner(UnicodeCache* unicode_cache) + : unicode_cache_(unicode_cache) { } + + +uc32 Scanner::ScanHexNumber(int expected_length) { + ASSERT(expected_length <= 4); // prevent overflow + + uc32 digits[4] = { 0, 0, 0, 0 }; + uc32 x = 0; + for (int i = 0; i < expected_length; i++) { + digits[i] = c0_; + int d = HexValue(c0_); + if (d < 0) { + // According to ECMA-262, 3rd, 7.8.4, page 18, these hex escapes + // should be illegal, but other JS VMs just return the + // non-escaped version of the original character. + + // Push back digits that we have advanced past. + for (int j = i-1; j >= 0; j--) { + PushBack(digits[j]); + } + return -1; + } + x = x * 16 + d; + Advance(); } - pos_--; + + return x; } -bool BufferedUC16CharacterStream::ReadBlock() { - buffer_cursor_ = buffer_; - if (pushback_limit_ != NULL) { - // Leave pushback mode. - buffer_end_ = pushback_limit_; - pushback_limit_ = NULL; - // If there were any valid characters left at the - // start of the buffer, use those. - if (buffer_cursor_ < buffer_end_) return true; - // Otherwise read a new block. + +// ---------------------------------------------------------------------------- +// JavaScriptScanner + +JavaScriptScanner::JavaScriptScanner(UnicodeCache* scanner_contants) + : Scanner(scanner_contants), + octal_pos_(Location::invalid()), + harmony_block_scoping_(false) { } + + +void JavaScriptScanner::Initialize(UC16CharacterStream* source) { + source_ = source; + // Need to capture identifiers in order to recognize "get" and "set" + // in object literals. + Init(); + // Skip initial whitespace allowing HTML comment ends just like + // after a newline and scan first token. + has_line_terminator_before_next_ = true; + SkipWhiteSpace(); + Scan(); +} + + +// Ensure that tokens can be stored in a byte. +STATIC_ASSERT(Token::NUM_TOKENS <= 0x100); + +// Table of one-character tokens, by character (0x00..0x7f only). +static const byte one_char_tokens[] = { + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::LPAREN, // 0x28 + Token::RPAREN, // 0x29 + Token::ILLEGAL, + Token::ILLEGAL, + Token::COMMA, // 0x2c + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::COLON, // 0x3a + Token::SEMICOLON, // 0x3b + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::CONDITIONAL, // 0x3f + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::LBRACK, // 0x5b + Token::ILLEGAL, + Token::RBRACK, // 0x5d + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::ILLEGAL, + Token::LBRACE, // 0x7b + Token::ILLEGAL, + Token::RBRACE, // 0x7d + Token::BIT_NOT, // 0x7e + Token::ILLEGAL +}; + + +Token::Value JavaScriptScanner::Next() { + current_ = next_; + has_line_terminator_before_next_ = false; + has_multiline_comment_before_next_ = false; + if (static_cast<unsigned>(c0_) <= 0x7f) { + Token::Value token = static_cast<Token::Value>(one_char_tokens[c0_]); + if (token != Token::ILLEGAL) { + int pos = source_pos(); + next_.token = token; + next_.location.beg_pos = pos; + next_.location.end_pos = pos + 1; + Advance(); + return current_.token; + } } - unsigned length = FillBuffer(pos_, kBufferSize); - buffer_end_ = buffer_ + length; - return length > 0; + Scan(); + return current_.token; } -unsigned BufferedUC16CharacterStream::SlowSeekForward(unsigned delta) { - // Leave pushback mode (i.e., ignore that there might be valid data - // in the buffer before the pushback_limit_ point). - pushback_limit_ = NULL; - return BufferSeekForward(delta); +static inline bool IsByteOrderMark(uc32 c) { + // The Unicode value U+FFFE is guaranteed never to be assigned as a + // Unicode character; this implies that in a Unicode context the + // 0xFF, 0xFE byte pattern can only be interpreted as the U+FEFF + // character expressed in little-endian byte order (since it could + // not be a U+FFFE character expressed in big-endian byte + // order). Nevertheless, we check for it to be compatible with + // Spidermonkey. + return c == 0xFEFF || c == 0xFFFE; } -// ---------------------------------------------------------------------------- -// GenericStringUC16CharacterStream - - -GenericStringUC16CharacterStream::GenericStringUC16CharacterStream( - Handle<String> data, - unsigned start_position, - unsigned end_position) - : string_(data), - length_(end_position) { - ASSERT(end_position >= start_position); - buffer_cursor_ = buffer_; - buffer_end_ = buffer_; - pos_ = start_position; + +bool JavaScriptScanner::SkipWhiteSpace() { + int start_position = source_pos(); + + while (true) { + // We treat byte-order marks (BOMs) as whitespace for better + // compatibility with Spidermonkey and other JavaScript engines. + while (unicode_cache_->IsWhiteSpace(c0_) || IsByteOrderMark(c0_)) { + // IsWhiteSpace() includes line terminators! + if (unicode_cache_->IsLineTerminator(c0_)) { + // Ignore line terminators, but remember them. This is necessary + // for automatic semicolon insertion. + has_line_terminator_before_next_ = true; + } + Advance(); + } + + // If there is an HTML comment end '-->' at the beginning of a + // line (with only whitespace in front of it), we treat the rest + // of the line as a comment. This is in line with the way + // SpiderMonkey handles it. + if (c0_ == '-' && has_line_terminator_before_next_) { + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>') { + // Treat the rest of the line as a comment. + SkipSingleLineComment(); + // Continue skipping white space after the comment. + continue; + } + PushBack('-'); // undo Advance() + } + PushBack('-'); // undo Advance() + } + // Return whether or not we skipped any characters. + return source_pos() != start_position; + } } -GenericStringUC16CharacterStream::~GenericStringUC16CharacterStream() { } +Token::Value JavaScriptScanner::SkipSingleLineComment() { + Advance(); + // The line terminator at the end of the line is not considered + // to be part of the single-line comment; it is recognized + // separately by the lexical grammar and becomes part of the + // stream of input elements for the syntactic grammar (see + // ECMA-262, section 7.4). + while (c0_ >= 0 && !unicode_cache_->IsLineTerminator(c0_)) { + Advance(); + } -unsigned GenericStringUC16CharacterStream::BufferSeekForward(unsigned delta) { - unsigned old_pos = pos_; - pos_ = Min(pos_ + delta, length_); - ReadBlock(); - return pos_ - old_pos; + return Token::WHITESPACE; } -unsigned GenericStringUC16CharacterStream::FillBuffer(unsigned from_pos, - unsigned length) { - if (from_pos >= length_) return 0; - if (from_pos + length > length_) { - length = length_ - from_pos; +Token::Value JavaScriptScanner::SkipMultiLineComment() { + ASSERT(c0_ == '*'); + Advance(); + + while (c0_ >= 0) { + uc32 ch = c0_; + Advance(); + if (unicode_cache_->IsLineTerminator(ch)) { + // Following ECMA-262, section 7.4, a comment containing + // a newline will make the comment count as a line-terminator. + has_multiline_comment_before_next_ = true; + } + // If we have reached the end of the multi-line comment, we + // consume the '/' and insert a whitespace. This way all + // multi-line comments are treated as whitespace. + if (ch == '*' && c0_ == '/') { + c0_ = ' '; + return Token::WHITESPACE; + } } - String::WriteToFlat<uc16>(*string_, buffer_, from_pos, from_pos + length); - return length; + + // Unterminated multi-line comment. + return Token::ILLEGAL; } -// ---------------------------------------------------------------------------- -// Utf8ToUC16CharacterStream -Utf8ToUC16CharacterStream::Utf8ToUC16CharacterStream(const byte* data, - unsigned length) - : BufferedUC16CharacterStream(), - raw_data_(data), - raw_data_length_(length), - raw_data_pos_(0), - raw_character_position_(0) { - ReadBlock(); +Token::Value JavaScriptScanner::ScanHtmlComment() { + // Check for <!-- comments. + ASSERT(c0_ == '!'); + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '-') return SkipSingleLineComment(); + PushBack('-'); // undo Advance() + } + PushBack('!'); // undo Advance() + ASSERT(c0_ == '!'); + return Token::LT; } -Utf8ToUC16CharacterStream::~Utf8ToUC16CharacterStream() { } +void JavaScriptScanner::Scan() { + next_.literal_chars = NULL; + Token::Value token; + do { + // Remember the position of the next token + next_.location.beg_pos = source_pos(); + + switch (c0_) { + case ' ': + case '\t': + Advance(); + token = Token::WHITESPACE; + break; + + case '\n': + Advance(); + has_line_terminator_before_next_ = true; + token = Token::WHITESPACE; + break; + + case '"': case '\'': + token = ScanString(); + break; + + case '<': + // < <= << <<= <!-- + Advance(); + if (c0_ == '=') { + token = Select(Token::LTE); + } else if (c0_ == '<') { + token = Select('=', Token::ASSIGN_SHL, Token::SHL); + } else if (c0_ == '!') { + token = ScanHtmlComment(); + } else { + token = Token::LT; + } + break; + + case '>': + // > >= >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(Token::GTE); + } else if (c0_ == '>') { + // >> >>= >>> >>>= + Advance(); + if (c0_ == '=') { + token = Select(Token::ASSIGN_SAR); + } else if (c0_ == '>') { + token = Select('=', Token::ASSIGN_SHR, Token::SHR); + } else { + token = Token::SAR; + } + } else { + token = Token::GT; + } + break; + + case '=': + // = == === + Advance(); + if (c0_ == '=') { + token = Select('=', Token::EQ_STRICT, Token::EQ); + } else { + token = Token::ASSIGN; + } + break; + + case '!': + // ! != !== + Advance(); + if (c0_ == '=') { + token = Select('=', Token::NE_STRICT, Token::NE); + } else { + token = Token::NOT; + } + break; + + case '+': + // + ++ += + Advance(); + if (c0_ == '+') { + token = Select(Token::INC); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_ADD); + } else { + token = Token::ADD; + } + break; + + case '-': + // - -- --> -= + Advance(); + if (c0_ == '-') { + Advance(); + if (c0_ == '>' && has_line_terminator_before_next_) { + // For compatibility with SpiderMonkey, we skip lines that + // start with an HTML comment end '-->'. + token = SkipSingleLineComment(); + } else { + token = Token::DEC; + } + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_SUB); + } else { + token = Token::SUB; + } + break; + + case '*': + // * *= + token = Select('=', Token::ASSIGN_MUL, Token::MUL); + break; + case '%': + // % %= + token = Select('=', Token::ASSIGN_MOD, Token::MOD); + break; -unsigned Utf8ToUC16CharacterStream::BufferSeekForward(unsigned delta) { - unsigned old_pos = pos_; - unsigned target_pos = pos_ + delta; - SetRawPosition(target_pos); - pos_ = raw_character_position_; - ReadBlock(); - return pos_ - old_pos; + case '/': + // / // /* /= + Advance(); + if (c0_ == '/') { + token = SkipSingleLineComment(); + } else if (c0_ == '*') { + token = SkipMultiLineComment(); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_DIV); + } else { + token = Token::DIV; + } + break; + + case '&': + // & && &= + Advance(); + if (c0_ == '&') { + token = Select(Token::AND); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_BIT_AND); + } else { + token = Token::BIT_AND; + } + break; + + case '|': + // | || |= + Advance(); + if (c0_ == '|') { + token = Select(Token::OR); + } else if (c0_ == '=') { + token = Select(Token::ASSIGN_BIT_OR); + } else { + token = Token::BIT_OR; + } + break; + + case '^': + // ^ ^= + token = Select('=', Token::ASSIGN_BIT_XOR, Token::BIT_XOR); + break; + + case '.': + // . Number + Advance(); + if (IsDecimalDigit(c0_)) { + token = ScanNumber(true); + } else { + token = Token::PERIOD; + } + break; + + case ':': + token = Select(Token::COLON); + break; + + case ';': + token = Select(Token::SEMICOLON); + break; + + case ',': + token = Select(Token::COMMA); + break; + + case '(': + token = Select(Token::LPAREN); + break; + + case ')': + token = Select(Token::RPAREN); + break; + + case '[': + token = Select(Token::LBRACK); + break; + + case ']': + token = Select(Token::RBRACK); + break; + + case '{': + token = Select(Token::LBRACE); + break; + + case '}': + token = Select(Token::RBRACE); + break; + + case '?': + token = Select(Token::CONDITIONAL); + break; + + case '~': + token = Select(Token::BIT_NOT); + break; + + default: + if (unicode_cache_->IsIdentifierStart(c0_)) { + token = ScanIdentifierOrKeyword(); + } else if (IsDecimalDigit(c0_)) { + token = ScanNumber(false); + } else if (SkipWhiteSpace()) { + token = Token::WHITESPACE; + } else if (c0_ < 0) { + token = Token::EOS; + } else { + token = Select(Token::ILLEGAL); + } + break; + } + + // Continue scanning for tokens as long as we're just skipping + // whitespace. + } while (token == Token::WHITESPACE); + + next_.location.end_pos = source_pos(); + next_.token = token; } -unsigned Utf8ToUC16CharacterStream::FillBuffer(unsigned char_position, - unsigned length) { - static const unibrow::uchar kMaxUC16Character = 0xffff; - SetRawPosition(char_position); - if (raw_character_position_ != char_position) { - // char_position was not a valid position in the stream (hit the end - // while spooling to it). - return 0u; +void JavaScriptScanner::SeekForward(int pos) { + // After this call, we will have the token at the given position as + // the "next" token. The "current" token will be invalid. + if (pos == next_.location.beg_pos) return; + int current_pos = source_pos(); + ASSERT_EQ(next_.location.end_pos, current_pos); + // Positions inside the lookahead token aren't supported. + ASSERT(pos >= current_pos); + if (pos != current_pos) { + source_->SeekForward(pos - source_->pos()); + Advance(); + // This function is only called to seek to the location + // of the end of a function (at the "}" token). It doesn't matter + // whether there was a line terminator in the part we skip. + has_line_terminator_before_next_ = false; + has_multiline_comment_before_next_ = false; } - unsigned i = 0; - while (i < length) { - if (raw_data_pos_ == raw_data_length_) break; - unibrow::uchar c = raw_data_[raw_data_pos_]; - if (c <= unibrow::Utf8::kMaxOneByteChar) { - raw_data_pos_++; - } else { - c = unibrow::Utf8::CalculateValue(raw_data_ + raw_data_pos_, - raw_data_length_ - raw_data_pos_, - &raw_data_pos_); - // Don't allow characters outside of the BMP. - if (c > kMaxUC16Character) { - c = unibrow::Utf8::kBadChar; - } + Scan(); +} + + +void JavaScriptScanner::ScanEscape() { + uc32 c = c0_; + Advance(); + + // Skip escaped newlines. + if (unicode_cache_->IsLineTerminator(c)) { + // Allow CR+LF newlines in multiline string literals. + if (IsCarriageReturn(c) && IsLineFeed(c0_)) Advance(); + // Allow LF+CR newlines in multiline string literals. + if (IsLineFeed(c) && IsCarriageReturn(c0_)) Advance(); + return; + } + + switch (c) { + case '\'': // fall through + case '"' : // fall through + case '\\': break; + case 'b' : c = '\b'; break; + case 'f' : c = '\f'; break; + case 'n' : c = '\n'; break; + case 'r' : c = '\r'; break; + case 't' : c = '\t'; break; + case 'u' : { + c = ScanHexNumber(4); + if (c < 0) c = 'u'; + break; + } + case 'v' : c = '\v'; break; + case 'x' : { + c = ScanHexNumber(2); + if (c < 0) c = 'x'; + break; } - buffer_[i++] = static_cast<uc16>(c); + case '0' : // fall through + case '1' : // fall through + case '2' : // fall through + case '3' : // fall through + case '4' : // fall through + case '5' : // fall through + case '6' : // fall through + case '7' : c = ScanOctalEscape(c, 2); break; } - raw_character_position_ = char_position + i; - return i; + + // According to ECMA-262, 3rd, 7.8.4 (p 18ff) these + // should be illegal, but they are commonly handled + // as non-escaped characters by JS VMs. + AddLiteralChar(c); } -static const byte kUtf8MultiByteMask = 0xC0; -static const byte kUtf8MultiByteCharStart = 0xC0; -static const byte kUtf8MultiByteCharFollower = 0x80; +// Octal escapes of the forms '\0xx' and '\xxx' are not a part of +// ECMA-262. Other JS VMs support them. +uc32 JavaScriptScanner::ScanOctalEscape(uc32 c, int length) { + uc32 x = c - '0'; + int i = 0; + for (; i < length; i++) { + int d = c0_ - '0'; + if (d < 0 || d > 7) break; + int nx = x * 8 + d; + if (nx >= 256) break; + x = nx; + Advance(); + } + // Anything except '\0' is an octal escape sequence, illegal in strict mode. + // Remember the position of octal escape sequences so that an error + // can be reported later (in strict mode). + // We don't report the error immediately, because the octal escape can + // occur before the "use strict" directive. + if (c != '0' || i > 0) { + octal_pos_ = Location(source_pos() - i - 1, source_pos() - 1); + } + return x; +} + + +Token::Value JavaScriptScanner::ScanString() { + uc32 quote = c0_; + Advance(); // consume quote + LiteralScope literal(this); + while (c0_ != quote && c0_ >= 0 + && !unicode_cache_->IsLineTerminator(c0_)) { + uc32 c = c0_; + Advance(); + if (c == '\\') { + if (c0_ < 0) return Token::ILLEGAL; + ScanEscape(); + } else { + AddLiteralChar(c); + } + } + if (c0_ != quote) return Token::ILLEGAL; + literal.Complete(); -#ifdef DEBUG -static bool IsUtf8MultiCharacterStart(byte first_byte) { - return (first_byte & kUtf8MultiByteMask) == kUtf8MultiByteCharStart; + Advance(); // consume quote + return Token::STRING; } -#endif -static bool IsUtf8MultiCharacterFollower(byte later_byte) { - return (later_byte & kUtf8MultiByteMask) == kUtf8MultiByteCharFollower; +void JavaScriptScanner::ScanDecimalDigits() { + while (IsDecimalDigit(c0_)) + AddLiteralCharAdvance(); } -// Move the cursor back to point at the preceding UTF-8 character start -// in the buffer. -static inline void Utf8CharacterBack(const byte* buffer, unsigned* cursor) { - byte character = buffer[--*cursor]; - if (character > unibrow::Utf8::kMaxOneByteChar) { - ASSERT(IsUtf8MultiCharacterFollower(character)); - // Last byte of a multi-byte character encoding. Step backwards until - // pointing to the first byte of the encoding, recognized by having the - // top two bits set. - while (IsUtf8MultiCharacterFollower(buffer[--*cursor])) { } - ASSERT(IsUtf8MultiCharacterStart(buffer[*cursor])); +Token::Value JavaScriptScanner::ScanNumber(bool seen_period) { + ASSERT(IsDecimalDigit(c0_)); // the first digit of the number or the fraction + + enum { DECIMAL, HEX, OCTAL } kind = DECIMAL; + + LiteralScope literal(this); + if (seen_period) { + // we have already seen a decimal point of the float + AddLiteralChar('.'); + ScanDecimalDigits(); // we know we have at least one digit + + } else { + // if the first character is '0' we must check for octals and hex + if (c0_ == '0') { + int start_pos = source_pos(); // For reporting octal positions. + AddLiteralCharAdvance(); + + // either 0, 0exxx, 0Exxx, 0.xxx, an octal number, or a hex number + if (c0_ == 'x' || c0_ == 'X') { + // hex number + kind = HEX; + AddLiteralCharAdvance(); + if (!IsHexDigit(c0_)) { + // we must have at least one hex digit after 'x'/'X' + return Token::ILLEGAL; + } + while (IsHexDigit(c0_)) { + AddLiteralCharAdvance(); + } + } else if ('0' <= c0_ && c0_ <= '7') { + // (possible) octal number + kind = OCTAL; + while (true) { + if (c0_ == '8' || c0_ == '9') { + kind = DECIMAL; + break; + } + if (c0_ < '0' || '7' < c0_) { + // Octal literal finished. + octal_pos_ = Location(start_pos, source_pos()); + break; + } + AddLiteralCharAdvance(); + } + } + } + + // Parse decimal digits and allow trailing fractional part. + if (kind == DECIMAL) { + ScanDecimalDigits(); // optional + if (c0_ == '.') { + AddLiteralCharAdvance(); + ScanDecimalDigits(); // optional + } + } } + + // scan exponent, if any + if (c0_ == 'e' || c0_ == 'E') { + ASSERT(kind != HEX); // 'e'/'E' must be scanned as part of the hex number + if (kind == OCTAL) return Token::ILLEGAL; // no exponent for octals allowed + // scan exponent + AddLiteralCharAdvance(); + if (c0_ == '+' || c0_ == '-') + AddLiteralCharAdvance(); + if (!IsDecimalDigit(c0_)) { + // we must have at least one decimal digit after 'e'/'E' + return Token::ILLEGAL; + } + ScanDecimalDigits(); + } + + // The source character immediately following a numeric literal must + // not be an identifier start or a decimal digit; see ECMA-262 + // section 7.8.3, page 17 (note that we read only one decimal digit + // if the value is 0). + if (IsDecimalDigit(c0_) || unicode_cache_->IsIdentifierStart(c0_)) + return Token::ILLEGAL; + + literal.Complete(); + + return Token::NUMBER; } -// Move the cursor forward to point at the next following UTF-8 character start -// in the buffer. -static inline void Utf8CharacterForward(const byte* buffer, unsigned* cursor) { - byte character = buffer[(*cursor)++]; - if (character > unibrow::Utf8::kMaxOneByteChar) { - // First character of a multi-byte character encoding. - // The number of most-significant one-bits determines the length of the - // encoding: - // 110..... - (0xCx, 0xDx) one additional byte (minimum). - // 1110.... - (0xEx) two additional bytes. - // 11110... - (0xFx) three additional bytes (maximum). - ASSERT(IsUtf8MultiCharacterStart(character)); - // Additional bytes is: - // 1 if value in range 0xC0 .. 0xDF. - // 2 if value in range 0xE0 .. 0xEF. - // 3 if value in range 0xF0 .. 0xF7. - // Encode that in a single value. - unsigned additional_bytes = - ((0x3211u) >> (((character - 0xC0) >> 2) & 0xC)) & 0x03; - *cursor += additional_bytes; - ASSERT(!IsUtf8MultiCharacterFollower(buffer[1 + additional_bytes])); +uc32 JavaScriptScanner::ScanIdentifierUnicodeEscape() { + Advance(); + if (c0_ != 'u') return -1; + Advance(); + uc32 result = ScanHexNumber(4); + if (result < 0) PushBack('u'); + return result; +} + + +// ---------------------------------------------------------------------------- +// Keyword Matcher + +#define KEYWORDS(KEYWORD_GROUP, KEYWORD) \ + KEYWORD_GROUP('b') \ + KEYWORD("break", Token::BREAK) \ + KEYWORD_GROUP('c') \ + KEYWORD("case", Token::CASE) \ + KEYWORD("catch", Token::CATCH) \ + KEYWORD("class", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("const", Token::CONST) \ + KEYWORD("continue", Token::CONTINUE) \ + KEYWORD_GROUP('d') \ + KEYWORD("debugger", Token::DEBUGGER) \ + KEYWORD("default", Token::DEFAULT) \ + KEYWORD("delete", Token::DELETE) \ + KEYWORD("do", Token::DO) \ + KEYWORD_GROUP('e') \ + KEYWORD("else", Token::ELSE) \ + KEYWORD("enum", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("export", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("extends", Token::FUTURE_RESERVED_WORD) \ + KEYWORD_GROUP('f') \ + KEYWORD("false", Token::FALSE_LITERAL) \ + KEYWORD("finally", Token::FINALLY) \ + KEYWORD("for", Token::FOR) \ + KEYWORD("function", Token::FUNCTION) \ + KEYWORD_GROUP('i') \ + KEYWORD("if", Token::IF) \ + KEYWORD("implements", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("import", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("in", Token::IN) \ + KEYWORD("instanceof", Token::INSTANCEOF) \ + KEYWORD("interface", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('l') \ + KEYWORD("let", harmony_block_scoping \ + ? Token::LET : Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('n') \ + KEYWORD("new", Token::NEW) \ + KEYWORD("null", Token::NULL_LITERAL) \ + KEYWORD_GROUP('p') \ + KEYWORD("package", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("private", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("protected", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("public", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD_GROUP('r') \ + KEYWORD("return", Token::RETURN) \ + KEYWORD_GROUP('s') \ + KEYWORD("static", Token::FUTURE_STRICT_RESERVED_WORD) \ + KEYWORD("super", Token::FUTURE_RESERVED_WORD) \ + KEYWORD("switch", Token::SWITCH) \ + KEYWORD_GROUP('t') \ + KEYWORD("this", Token::THIS) \ + KEYWORD("throw", Token::THROW) \ + KEYWORD("true", Token::TRUE_LITERAL) \ + KEYWORD("try", Token::TRY) \ + KEYWORD("typeof", Token::TYPEOF) \ + KEYWORD_GROUP('v') \ + KEYWORD("var", Token::VAR) \ + KEYWORD("void", Token::VOID) \ + KEYWORD_GROUP('w') \ + KEYWORD("while", Token::WHILE) \ + KEYWORD("with", Token::WITH) \ + KEYWORD_GROUP('y') \ + KEYWORD("yield", Token::FUTURE_STRICT_RESERVED_WORD) + + +static Token::Value KeywordOrIdentifierToken(const char* input, + int input_length, + bool harmony_block_scoping) { + ASSERT(input_length >= 1); + const int kMinLength = 2; + const int kMaxLength = 10; + if (input_length < kMinLength || input_length > kMaxLength) { + return Token::IDENTIFIER; } + switch (input[0]) { + default: +#define KEYWORD_GROUP_CASE(ch) \ + break; \ + case ch: +#define KEYWORD(keyword, token) \ + { \ + /* 'keyword' is a char array, so sizeof(keyword) is */ \ + /* strlen(keyword) plus 1 for the NUL char. */ \ + const int keyword_length = sizeof(keyword) - 1; \ + STATIC_ASSERT(keyword_length >= kMinLength); \ + STATIC_ASSERT(keyword_length <= kMaxLength); \ + if (input_length == keyword_length && \ + input[1] == keyword[1] && \ + (keyword_length <= 2 || input[2] == keyword[2]) && \ + (keyword_length <= 3 || input[3] == keyword[3]) && \ + (keyword_length <= 4 || input[4] == keyword[4]) && \ + (keyword_length <= 5 || input[5] == keyword[5]) && \ + (keyword_length <= 6 || input[6] == keyword[6]) && \ + (keyword_length <= 7 || input[7] == keyword[7]) && \ + (keyword_length <= 8 || input[8] == keyword[8]) && \ + (keyword_length <= 9 || input[9] == keyword[9])) { \ + return token; \ + } \ + } + KEYWORDS(KEYWORD_GROUP_CASE, KEYWORD) + } + return Token::IDENTIFIER; } -void Utf8ToUC16CharacterStream::SetRawPosition(unsigned target_position) { - if (raw_character_position_ > target_position) { - // Spool backwards in utf8 buffer. - do { - Utf8CharacterBack(raw_data_, &raw_data_pos_); - raw_character_position_--; - } while (raw_character_position_ > target_position); - return; +Token::Value JavaScriptScanner::ScanIdentifierOrKeyword() { + ASSERT(unicode_cache_->IsIdentifierStart(c0_)); + LiteralScope literal(this); + // Scan identifier start character. + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + // Only allow legal identifier start characters. + if (c < 0 || + c == '\\' || // No recursive escapes. + !unicode_cache_->IsIdentifierStart(c)) { + return Token::ILLEGAL; + } + AddLiteralChar(c); + return ScanIdentifierSuffix(&literal); } - // Spool forwards in the utf8 buffer. - while (raw_character_position_ < target_position) { - if (raw_data_pos_ == raw_data_length_) return; - Utf8CharacterForward(raw_data_, &raw_data_pos_); - raw_character_position_++; + + uc32 first_char = c0_; + Advance(); + AddLiteralChar(first_char); + + // Scan the rest of the identifier characters. + while (unicode_cache_->IsIdentifierPart(c0_)) { + if (c0_ != '\\') { + uc32 next_char = c0_; + Advance(); + AddLiteralChar(next_char); + continue; + } + // Fallthrough if no longer able to complete keyword. + return ScanIdentifierSuffix(&literal); } + + literal.Complete(); + + if (next_.literal_chars->is_ascii()) { + Vector<const char> chars = next_.literal_chars->ascii_literal(); + return KeywordOrIdentifierToken(chars.start(), + chars.length(), + harmony_block_scoping_); + } + + return Token::IDENTIFIER; } -// ---------------------------------------------------------------------------- -// ExternalTwoByteStringUC16CharacterStream - -ExternalTwoByteStringUC16CharacterStream:: - ~ExternalTwoByteStringUC16CharacterStream() { } - - -ExternalTwoByteStringUC16CharacterStream - ::ExternalTwoByteStringUC16CharacterStream( - Handle<ExternalTwoByteString> data, - int start_position, - int end_position) - : UC16CharacterStream(), - source_(data), - raw_data_(data->GetTwoByteData(start_position)) { - buffer_cursor_ = raw_data_, - buffer_end_ = raw_data_ + (end_position - start_position); - pos_ = start_position; +Token::Value JavaScriptScanner::ScanIdentifierSuffix(LiteralScope* literal) { + // Scan the rest of the identifier characters. + while (unicode_cache_->IsIdentifierPart(c0_)) { + if (c0_ == '\\') { + uc32 c = ScanIdentifierUnicodeEscape(); + // Only allow legal identifier part characters. + if (c < 0 || + c == '\\' || + !unicode_cache_->IsIdentifierPart(c)) { + return Token::ILLEGAL; + } + AddLiteralChar(c); + } else { + AddLiteralChar(c0_); + Advance(); + } + } + literal->Complete(); + + return Token::IDENTIFIER; } -// ---------------------------------------------------------------------------- -// Scanner::LiteralScope +bool JavaScriptScanner::ScanRegExpPattern(bool seen_equal) { + // Scan: ('/' | '/=') RegularExpressionBody '/' RegularExpressionFlags + bool in_character_class = false; -Scanner::LiteralScope::LiteralScope(Scanner* self) - : scanner_(self), complete_(false) { - self->StartLiteral(); + // Previous token is either '/' or '/=', in the second case, the + // pattern starts at =. + next_.location.beg_pos = source_pos() - (seen_equal ? 2 : 1); + next_.location.end_pos = source_pos() - (seen_equal ? 1 : 0); + + // Scan regular expression body: According to ECMA-262, 3rd, 7.8.5, + // the scanner should pass uninterpreted bodies to the RegExp + // constructor. + LiteralScope literal(this); + if (seen_equal) { + AddLiteralChar('='); + } + + while (c0_ != '/' || in_character_class) { + if (unicode_cache_->IsLineTerminator(c0_) || c0_ < 0) return false; + if (c0_ == '\\') { // Escape sequence. + AddLiteralCharAdvance(); + if (unicode_cache_->IsLineTerminator(c0_) || c0_ < 0) return false; + AddLiteralCharAdvance(); + // If the escape allows more characters, i.e., \x??, \u????, or \c?, + // only "safe" characters are allowed (letters, digits, underscore), + // otherwise the escape isn't valid and the invalid character has + // its normal meaning. I.e., we can just continue scanning without + // worrying whether the following characters are part of the escape + // or not, since any '/', '\\' or '[' is guaranteed to not be part + // of the escape sequence. + + // TODO(896): At some point, parse RegExps more throughly to capture + // octal esacpes in strict mode. + } else { // Unescaped character. + if (c0_ == '[') in_character_class = true; + if (c0_ == ']') in_character_class = false; + AddLiteralCharAdvance(); + } + } + Advance(); // consume '/' + + literal.Complete(); + + return true; } -Scanner::LiteralScope::~LiteralScope() { - if (!complete_) scanner_->DropLiteral(); +bool JavaScriptScanner::ScanLiteralUnicodeEscape() { + ASSERT(c0_ == '\\'); + uc32 chars_read[6] = {'\\', 'u', 0, 0, 0, 0}; + Advance(); + int i = 1; + if (c0_ == 'u') { + i++; + while (i < 6) { + Advance(); + if (!IsHexDigit(c0_)) break; + chars_read[i] = c0_; + i++; + } + } + if (i < 6) { + // Incomplete escape. Undo all advances and return false. + while (i > 0) { + i--; + PushBack(chars_read[i]); + } + return false; + } + // Complete escape. Add all chars to current literal buffer. + for (int i = 0; i < 6; i++) { + AddLiteralChar(chars_read[i]); + } + return true; } -void Scanner::LiteralScope::Complete() { - scanner_->TerminateLiteral(); - complete_ = true; +bool JavaScriptScanner::ScanRegExpFlags() { + // Scan regular expression flags. + LiteralScope literal(this); + while (unicode_cache_->IsIdentifierPart(c0_)) { + if (c0_ != '\\') { + AddLiteralCharAdvance(); + } else { + if (!ScanLiteralUnicodeEscape()) { + break; + } + } + } + literal.Complete(); + + next_.location.end_pos = source_pos() - 1; + return true; } } } // namespace v8::internal diff --git a/src/scanner.h b/src/scanner.h index e66dd60d..16c3a427 100644 --- a/src/scanner.h +++ b/src/scanner.h @@ -25,105 +25,538 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// Features shared by parsing and pre-parsing scanners. + #ifndef V8_SCANNER_H_ #define V8_SCANNER_H_ +#include "allocation.h" +#include "char-predicates.h" +#include "checks.h" +#include "globals.h" #include "token.h" -#include "char-predicates-inl.h" -#include "scanner-base.h" +#include "unicode-inl.h" +#include "utils.h" namespace v8 { namespace internal { -// A buffered character stream based on a random access character -// source (ReadBlock can be called with pos_ pointing to any position, -// even positions before the current). -class BufferedUC16CharacterStream: public UC16CharacterStream { +// Returns the value (0 .. 15) of a hexadecimal character c. +// If c is not a legal hexadecimal character, returns a value < 0. +inline int HexValue(uc32 c) { + c -= '0'; + if (static_cast<unsigned>(c) <= 9) return c; + c = (c | 0x20) - ('a' - '0'); // detect 0x11..0x16 and 0x31..0x36. + if (static_cast<unsigned>(c) <= 5) return c + 10; + return -1; +} + + +// --------------------------------------------------------------------- +// Buffered stream of characters, using an internal UC16 buffer. + +class UC16CharacterStream { public: - BufferedUC16CharacterStream(); - virtual ~BufferedUC16CharacterStream(); + UC16CharacterStream() : pos_(0) { } + virtual ~UC16CharacterStream() { } + + // Returns and advances past the next UC16 character in the input + // stream. If there are no more characters, it returns a negative + // value. + inline uc32 Advance() { + if (buffer_cursor_ < buffer_end_ || ReadBlock()) { + pos_++; + return static_cast<uc32>(*(buffer_cursor_++)); + } + // Note: currently the following increment is necessary to avoid a + // parser problem! The scanner treats the final kEndOfInput as + // a character with a position, and does math relative to that + // position. + pos_++; + + return kEndOfInput; + } - virtual void PushBack(uc32 character); + // Return the current position in the character stream. + // Starts at zero. + inline unsigned pos() const { return pos_; } + + // Skips forward past the next character_count UC16 characters + // in the input, or until the end of input if that comes sooner. + // Returns the number of characters actually skipped. If less + // than character_count, + inline unsigned SeekForward(unsigned character_count) { + unsigned buffered_chars = + static_cast<unsigned>(buffer_end_ - buffer_cursor_); + if (character_count <= buffered_chars) { + buffer_cursor_ += character_count; + pos_ += character_count; + return character_count; + } + return SlowSeekForward(character_count); + } + + // Pushes back the most recently read UC16 character (or negative + // value if at end of input), i.e., the value returned by the most recent + // call to Advance. + // Must not be used right after calling SeekForward. + virtual void PushBack(int32_t character) = 0; protected: - static const unsigned kBufferSize = 512; - static const unsigned kPushBackStepSize = 16; + static const uc32 kEndOfInput = -1; + + // Ensures that the buffer_cursor_ points to the character at + // position pos_ of the input, if possible. If the position + // is at or after the end of the input, return false. If there + // are more characters available, return true. + virtual bool ReadBlock() = 0; + virtual unsigned SlowSeekForward(unsigned character_count) = 0; + + const uc16* buffer_cursor_; + const uc16* buffer_end_; + unsigned pos_; +}; + - virtual unsigned SlowSeekForward(unsigned delta); - virtual bool ReadBlock(); - virtual void SlowPushBack(uc16 character); +class UnicodeCache { +// --------------------------------------------------------------------- +// Caching predicates used by scanners. + public: + UnicodeCache() {} + typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; + + StaticResource<Utf8Decoder>* utf8_decoder() { + return &utf8_decoder_; + } + + bool IsIdentifierStart(unibrow::uchar c) { return kIsIdentifierStart.get(c); } + bool IsIdentifierPart(unibrow::uchar c) { return kIsIdentifierPart.get(c); } + bool IsLineTerminator(unibrow::uchar c) { return kIsLineTerminator.get(c); } + bool IsWhiteSpace(unibrow::uchar c) { return kIsWhiteSpace.get(c); } - virtual unsigned BufferSeekForward(unsigned delta) = 0; - virtual unsigned FillBuffer(unsigned position, unsigned length) = 0; + private: + unibrow::Predicate<IdentifierStart, 128> kIsIdentifierStart; + unibrow::Predicate<IdentifierPart, 128> kIsIdentifierPart; + unibrow::Predicate<unibrow::LineTerminator, 128> kIsLineTerminator; + unibrow::Predicate<unibrow::WhiteSpace, 128> kIsWhiteSpace; + StaticResource<Utf8Decoder> utf8_decoder_; - const uc16* pushback_limit_; - uc16 buffer_[kBufferSize]; + DISALLOW_COPY_AND_ASSIGN(UnicodeCache); }; -// Generic string stream. -class GenericStringUC16CharacterStream: public BufferedUC16CharacterStream { +// ---------------------------------------------------------------------------- +// LiteralBuffer - Collector of chars of literals. + +class LiteralBuffer { public: - GenericStringUC16CharacterStream(Handle<String> data, - unsigned start_position, - unsigned end_position); - virtual ~GenericStringUC16CharacterStream(); + LiteralBuffer() : is_ascii_(true), position_(0), backing_store_() { } - protected: - virtual unsigned BufferSeekForward(unsigned delta); - virtual unsigned FillBuffer(unsigned position, unsigned length); + ~LiteralBuffer() { + if (backing_store_.length() > 0) { + backing_store_.Dispose(); + } + } + + inline void AddChar(uc16 character) { + if (position_ >= backing_store_.length()) ExpandBuffer(); + if (is_ascii_) { + if (character < kMaxAsciiCharCodeU) { + backing_store_[position_] = static_cast<byte>(character); + position_ += kASCIISize; + return; + } + ConvertToUC16(); + } + *reinterpret_cast<uc16*>(&backing_store_[position_]) = character; + position_ += kUC16Size; + } + + bool is_ascii() { return is_ascii_; } + + Vector<const uc16> uc16_literal() { + ASSERT(!is_ascii_); + ASSERT((position_ & 0x1) == 0); + return Vector<const uc16>( + reinterpret_cast<const uc16*>(backing_store_.start()), + position_ >> 1); + } + + Vector<const char> ascii_literal() { + ASSERT(is_ascii_); + return Vector<const char>( + reinterpret_cast<const char*>(backing_store_.start()), + position_); + } - Handle<String> string_; - unsigned start_position_; - unsigned length_; + int length() { + return is_ascii_ ? position_ : (position_ >> 1); + } + + void Reset() { + position_ = 0; + is_ascii_ = true; + } + + private: + static const int kInitialCapacity = 16; + static const int kGrowthFactory = 4; + static const int kMinConversionSlack = 256; + static const int kMaxGrowth = 1 * MB; + inline int NewCapacity(int min_capacity) { + int capacity = Max(min_capacity, backing_store_.length()); + int new_capacity = Min(capacity * kGrowthFactory, capacity + kMaxGrowth); + return new_capacity; + } + + void ExpandBuffer() { + Vector<byte> new_store = Vector<byte>::New(NewCapacity(kInitialCapacity)); + memcpy(new_store.start(), backing_store_.start(), position_); + backing_store_.Dispose(); + backing_store_ = new_store; + } + + void ConvertToUC16() { + ASSERT(is_ascii_); + Vector<byte> new_store; + int new_content_size = position_ * kUC16Size; + if (new_content_size >= backing_store_.length()) { + // Ensure room for all currently read characters as UC16 as well + // as the character about to be stored. + new_store = Vector<byte>::New(NewCapacity(new_content_size)); + } else { + new_store = backing_store_; + } + char* src = reinterpret_cast<char*>(backing_store_.start()); + uc16* dst = reinterpret_cast<uc16*>(new_store.start()); + for (int i = position_ - 1; i >= 0; i--) { + dst[i] = src[i]; + } + if (new_store.start() != backing_store_.start()) { + backing_store_.Dispose(); + backing_store_ = new_store; + } + position_ = new_content_size; + is_ascii_ = false; + } + + bool is_ascii_; + int position_; + Vector<byte> backing_store_; + + DISALLOW_COPY_AND_ASSIGN(LiteralBuffer); }; -// UC16 stream based on a literal UTF-8 string. -class Utf8ToUC16CharacterStream: public BufferedUC16CharacterStream { +// ---------------------------------------------------------------------------- +// Scanner base-class. + +// Generic functionality used by both JSON and JavaScript scanners. +class Scanner { public: - Utf8ToUC16CharacterStream(const byte* data, unsigned length); - virtual ~Utf8ToUC16CharacterStream(); + // -1 is outside of the range of any real source code. + static const int kNoOctalLocation = -1; + + typedef unibrow::Utf8InputBuffer<1024> Utf8Decoder; + + class LiteralScope { + public: + explicit LiteralScope(Scanner* self); + ~LiteralScope(); + void Complete(); + + private: + Scanner* scanner_; + bool complete_; + }; + + explicit Scanner(UnicodeCache* scanner_contants); + + // Returns the current token again. + Token::Value current_token() { return current_.token; } + + // One token look-ahead (past the token returned by Next()). + Token::Value peek() const { return next_.token; } + + struct Location { + Location(int b, int e) : beg_pos(b), end_pos(e) { } + Location() : beg_pos(0), end_pos(0) { } + + bool IsValid() const { + return beg_pos >= 0 && end_pos >= beg_pos; + } + + static Location invalid() { return Location(-1, -1); } + + int beg_pos; + int end_pos; + }; + + // Returns the location information for the current token + // (the token returned by Next()). + Location location() const { return current_.location; } + Location peek_location() const { return next_.location; } + + // Returns the literal string, if any, for the current token (the + // token returned by Next()). The string is 0-terminated and in + // UTF-8 format; they may contain 0-characters. Literal strings are + // collected for identifiers, strings, and numbers. + // These functions only give the correct result if the literal + // was scanned between calls to StartLiteral() and TerminateLiteral(). + bool is_literal_ascii() { + ASSERT_NOT_NULL(current_.literal_chars); + return current_.literal_chars->is_ascii(); + } + Vector<const char> literal_ascii_string() { + ASSERT_NOT_NULL(current_.literal_chars); + return current_.literal_chars->ascii_literal(); + } + Vector<const uc16> literal_uc16_string() { + ASSERT_NOT_NULL(current_.literal_chars); + return current_.literal_chars->uc16_literal(); + } + int literal_length() const { + ASSERT_NOT_NULL(current_.literal_chars); + return current_.literal_chars->length(); + } + + bool literal_contains_escapes() const { + Location location = current_.location; + int source_length = (location.end_pos - location.beg_pos); + if (current_.token == Token::STRING) { + // Subtract delimiters. + source_length -= 2; + } + return current_.literal_chars->length() != source_length; + } + + // Returns the literal string for the next token (the token that + // would be returned if Next() were called). + bool is_next_literal_ascii() { + ASSERT_NOT_NULL(next_.literal_chars); + return next_.literal_chars->is_ascii(); + } + Vector<const char> next_literal_ascii_string() { + ASSERT_NOT_NULL(next_.literal_chars); + return next_.literal_chars->ascii_literal(); + } + Vector<const uc16> next_literal_uc16_string() { + ASSERT_NOT_NULL(next_.literal_chars); + return next_.literal_chars->uc16_literal(); + } + int next_literal_length() const { + ASSERT_NOT_NULL(next_.literal_chars); + return next_.literal_chars->length(); + } + + UnicodeCache* unicode_cache() { return unicode_cache_; } + + static const int kCharacterLookaheadBufferSize = 1; protected: - virtual unsigned BufferSeekForward(unsigned delta); - virtual unsigned FillBuffer(unsigned char_position, unsigned length); - void SetRawPosition(unsigned char_position); - - const byte* raw_data_; - unsigned raw_data_length_; // Measured in bytes, not characters. - unsigned raw_data_pos_; - // The character position of the character at raw_data[raw_data_pos_]. - // Not necessarily the same as pos_. - unsigned raw_character_position_; + // The current and look-ahead token. + struct TokenDesc { + Token::Value token; + Location location; + LiteralBuffer* literal_chars; + }; + + // Call this after setting source_ to the input. + void Init() { + // Set c0_ (one character ahead) + STATIC_ASSERT(kCharacterLookaheadBufferSize == 1); + Advance(); + // Initialize current_ to not refer to a literal. + current_.literal_chars = NULL; + } + + // Literal buffer support + inline void StartLiteral() { + LiteralBuffer* free_buffer = (current_.literal_chars == &literal_buffer1_) ? + &literal_buffer2_ : &literal_buffer1_; + free_buffer->Reset(); + next_.literal_chars = free_buffer; + } + + inline void AddLiteralChar(uc32 c) { + ASSERT_NOT_NULL(next_.literal_chars); + next_.literal_chars->AddChar(c); + } + + // Complete scanning of a literal. + inline void TerminateLiteral() { + // Does nothing in the current implementation. + } + + // Stops scanning of a literal and drop the collected characters, + // e.g., due to an encountered error. + inline void DropLiteral() { + next_.literal_chars = NULL; + } + + inline void AddLiteralCharAdvance() { + AddLiteralChar(c0_); + Advance(); + } + + // Low-level scanning support. + void Advance() { c0_ = source_->Advance(); } + void PushBack(uc32 ch) { + source_->PushBack(c0_); + c0_ = ch; + } + + inline Token::Value Select(Token::Value tok) { + Advance(); + return tok; + } + + inline Token::Value Select(uc32 next, Token::Value then, Token::Value else_) { + Advance(); + if (c0_ == next) { + Advance(); + return then; + } else { + return else_; + } + } + + uc32 ScanHexNumber(int expected_length); + + // Return the current source position. + int source_pos() { + return source_->pos() - kCharacterLookaheadBufferSize; + } + + UnicodeCache* unicode_cache_; + + // Buffers collecting literal strings, numbers, etc. + LiteralBuffer literal_buffer1_; + LiteralBuffer literal_buffer2_; + + TokenDesc current_; // desc for current token (as returned by Next()) + TokenDesc next_; // desc for next token (one token look-ahead) + + // Input stream. Must be initialized to an UC16CharacterStream. + UC16CharacterStream* source_; + + // One Unicode character look-ahead; c0_ < 0 at the end of the input. + uc32 c0_; }; +// ---------------------------------------------------------------------------- +// JavaScriptScanner - base logic for JavaScript scanning. -// UTF16 buffer to read characters from an external string. -class ExternalTwoByteStringUC16CharacterStream: public UC16CharacterStream { +class JavaScriptScanner : public Scanner { public: - ExternalTwoByteStringUC16CharacterStream(Handle<ExternalTwoByteString> data, - int start_position, - int end_position); - virtual ~ExternalTwoByteStringUC16CharacterStream(); + // A LiteralScope that disables recording of some types of JavaScript + // literals. If the scanner is configured to not record the specific + // type of literal, the scope will not call StartLiteral. + class LiteralScope { + public: + explicit LiteralScope(JavaScriptScanner* self) + : scanner_(self), complete_(false) { + scanner_->StartLiteral(); + } + ~LiteralScope() { + if (!complete_) scanner_->DropLiteral(); + } + void Complete() { + scanner_->TerminateLiteral(); + complete_ = true; + } - virtual void PushBack(uc32 character) { - ASSERT(buffer_cursor_ > raw_data_); - buffer_cursor_--; - pos_--; + private: + JavaScriptScanner* scanner_; + bool complete_; + }; + + explicit JavaScriptScanner(UnicodeCache* scanner_contants); + + void Initialize(UC16CharacterStream* source); + + // Returns the next token. + Token::Value Next(); + + // Returns true if there was a line terminator before the peek'ed token, + // possibly inside a multi-line comment. + bool HasAnyLineTerminatorBeforeNext() const { + return has_line_terminator_before_next_ || + has_multiline_comment_before_next_; } - protected: - virtual unsigned SlowSeekForward(unsigned delta) { - // Fast case always handles seeking. - return 0; + // Scans the input as a regular expression pattern, previous + // character(s) must be /(=). Returns true if a pattern is scanned. + bool ScanRegExpPattern(bool seen_equal); + // Returns true if regexp flags are scanned (always since flags can + // be empty). + bool ScanRegExpFlags(); + + // Tells whether the buffer contains an identifier (no escapes). + // Used for checking if a property name is an identifier. + static bool IsIdentifier(unibrow::CharacterStream* buffer); + + // Scans octal escape sequence. Also accepts "\0" decimal escape sequence. + uc32 ScanOctalEscape(uc32 c, int length); + + // Returns the location of the last seen octal literal + Location octal_position() const { return octal_pos_; } + void clear_octal_position() { octal_pos_ = Location::invalid(); } + + // Seek forward to the given position. This operation does not + // work in general, for instance when there are pushed back + // characters, but works for seeking forward until simple delimiter + // tokens, which is what it is used for. + void SeekForward(int pos); + + bool HarmonyBlockScoping() const { + return harmony_block_scoping_; } - virtual bool ReadBlock() { - // Entire string is read at start. - return false; + void SetHarmonyBlockScoping(bool block_scoping) { + harmony_block_scoping_ = block_scoping; } - Handle<ExternalTwoByteString> source_; - const uc16* raw_data_; // Pointer to the actual array of characters. + + + protected: + bool SkipWhiteSpace(); + Token::Value SkipSingleLineComment(); + Token::Value SkipMultiLineComment(); + + // Scans a single JavaScript token. + void Scan(); + + void ScanDecimalDigits(); + Token::Value ScanNumber(bool seen_period); + Token::Value ScanIdentifierOrKeyword(); + Token::Value ScanIdentifierSuffix(LiteralScope* literal); + + void ScanEscape(); + Token::Value ScanString(); + + // Scans a possible HTML comment -- begins with '<!'. + Token::Value ScanHtmlComment(); + + // Decodes a unicode escape-sequence which is part of an identifier. + // If the escape sequence cannot be decoded the result is kBadChar. + uc32 ScanIdentifierUnicodeEscape(); + // Recognizes a uniocde escape-sequence and adds its characters, + // uninterpreted, to the current literal. Used for parsing RegExp + // flags. + bool ScanLiteralUnicodeEscape(); + + // Start position of the octal literal last scanned. + Location octal_pos_; + + // Whether there is a line terminator whitespace character after + // the current token, and before the next. Does not count newlines + // inside multiline comments. + bool has_line_terminator_before_next_; + // Whether there is a multi-line comment that contains a + // line-terminator after the current token, and before the next. + bool has_multiline_comment_before_next_; + // Whether we scan 'let' as a keyword for harmony block scoped + // let bindings. + bool harmony_block_scoping_; }; } } // namespace v8::internal diff --git a/src/scopeinfo.cc b/src/scopeinfo.cc index 0eacc83c..ad31ca47 100644 --- a/src/scopeinfo.cc +++ b/src/scopeinfo.cc @@ -39,12 +39,8 @@ namespace internal { static int CompareLocal(Variable* const* v, Variable* const* w) { - Slot* s = (*v)->AsSlot(); - Slot* t = (*w)->AsSlot(); - // We may have rewritten parameters (that are in the arguments object) - // and which may have a NULL slot... - find a better solution... - int x = (s != NULL ? s->index() : 0); - int y = (t != NULL ? t->index() : 0); + int x = (*v)->index(); + int y = (*w)->index(); // Consider sorting them according to type as well? return x - y; } @@ -86,27 +82,24 @@ ScopeInfo<Allocator>::ScopeInfo(Scope* scope) for (int i = 0; i < locals.length(); i++) { Variable* var = locals[i]; if (var->is_used()) { - Slot* slot = var->AsSlot(); - if (slot != NULL) { - switch (slot->type()) { - case Slot::PARAMETER: - // explicitly added to parameters_ above - ignore - break; - - case Slot::LOCAL: - ASSERT(stack_slots_.length() == slot->index()); - stack_slots_.Add(var->name()); - break; - - case Slot::CONTEXT: - heap_locals.Add(var); - break; - - case Slot::LOOKUP: - // This is currently not used. - UNREACHABLE(); - break; - } + switch (var->location()) { + case Variable::UNALLOCATED: + case Variable::PARAMETER: + break; + + case Variable::LOCAL: + ASSERT(stack_slots_.length() == var->index()); + stack_slots_.Add(var->name()); + break; + + case Variable::CONTEXT: + heap_locals.Add(var); + break; + + case Variable::LOOKUP: + // We don't expect lookup variables in the locals list. + UNREACHABLE(); + break; } } } @@ -115,9 +108,9 @@ ScopeInfo<Allocator>::ScopeInfo(Scope* scope) if (scope->num_heap_slots() > 0) { // Add user-defined slots. for (int i = 0; i < heap_locals.length(); i++) { - ASSERT(heap_locals[i]->AsSlot()->index() - Context::MIN_CONTEXT_SLOTS == + ASSERT(heap_locals[i]->index() - Context::MIN_CONTEXT_SLOTS == context_slots_.length()); - ASSERT(heap_locals[i]->AsSlot()->index() - Context::MIN_CONTEXT_SLOTS == + ASSERT(heap_locals[i]->index() - Context::MIN_CONTEXT_SLOTS == context_modes_.length()); context_slots_.Add(heap_locals[i]->name()); context_modes_.Add(heap_locals[i]->mode()); @@ -131,18 +124,18 @@ ScopeInfo<Allocator>::ScopeInfo(Scope* scope) // For now, this must happen at the very end because of the // ordering of the scope info slots and the respective slot indices. if (scope->is_function_scope()) { - Variable* var = scope->function(); - if (var != NULL && - var->is_used() && - var->AsSlot()->type() == Slot::CONTEXT) { - function_name_ = var->name(); + VariableProxy* proxy = scope->function(); + if (proxy != NULL && + proxy->var()->is_used() && + proxy->var()->IsContextSlot()) { + function_name_ = proxy->name(); // Note that we must not find the function name in the context slot // list - instead it must be handled separately in the // Contexts::Lookup() function. Thus record an empty symbol here so we // get the correct number of context slots. - ASSERT(var->AsSlot()->index() - Context::MIN_CONTEXT_SLOTS == + ASSERT(proxy->var()->index() - Context::MIN_CONTEXT_SLOTS == context_slots_.length()); - ASSERT(var->AsSlot()->index() - Context::MIN_CONTEXT_SLOTS == + ASSERT(proxy->var()->index() - Context::MIN_CONTEXT_SLOTS == context_modes_.length()); context_slots_.Add(FACTORY->empty_symbol()); context_modes_.Add(Variable::INTERNAL); diff --git a/src/scopeinfo.h b/src/scopeinfo.h index 1c61f111..40c5c8a6 100644 --- a/src/scopeinfo.h +++ b/src/scopeinfo.h @@ -156,7 +156,6 @@ class SerializedScopeInfo : public FixedArray { static SerializedScopeInfo* Empty(); private: - inline Object** ContextEntriesAddr(); inline Object** ParameterEntriesAddr(); @@ -187,6 +186,7 @@ class ContextSlotCache { void Clear(); static const int kNotFound = -2; + private: ContextSlotCache() { for (int i = 0; i < kLength; ++i) { diff --git a/src/scopes.cc b/src/scopes.cc index a76492e0..d5a7a9f9 100644 --- a/src/scopes.cc +++ b/src/scopes.cc @@ -31,7 +31,6 @@ #include "bootstrapper.h" #include "compiler.h" -#include "prettyprinter.h" #include "scopeinfo.h" #include "allocation-inl.h" @@ -314,7 +313,7 @@ void Scope::Initialize(bool inside_with) { Variable::VAR, false, Variable::THIS); - var->set_rewrite(NewSlot(var, Slot::PARAMETER, -1)); + var->AllocateTo(Variable::PARAMETER, -1); receiver_ = var; } @@ -331,6 +330,35 @@ void Scope::Initialize(bool inside_with) { } +Scope* Scope::FinalizeBlockScope() { + ASSERT(is_block_scope()); + ASSERT(temps_.is_empty()); + ASSERT(params_.is_empty()); + + if (num_var_or_const() > 0) return this; + + // Remove this scope from outer scope. + for (int i = 0; i < outer_scope_->inner_scopes_.length(); i++) { + if (outer_scope_->inner_scopes_[i] == this) { + outer_scope_->inner_scopes_.Remove(i); + break; + } + } + + // Reparent inner scopes. + for (int i = 0; i < inner_scopes_.length(); i++) { + outer_scope()->AddInnerScope(inner_scopes_[i]); + } + + // Move unresolved variables + for (int i = 0; i < unresolved_.length(); i++) { + outer_scope()->unresolved_.Add(unresolved_[i]); + } + + return NULL; +} + + Variable* Scope::LocalLookup(Handle<String> name) { Variable* result = variables_.Lookup(name); if (result != NULL || scope_info_.is_null()) { @@ -360,7 +388,7 @@ Variable* Scope::LocalLookup(Handle<String> name) { Variable* var = variables_.Declare(this, name, mode, true, Variable::NORMAL); - var->set_rewrite(NewSlot(var, Slot::CONTEXT, index)); + var->AllocateTo(Variable::CONTEXT, index); return var; } @@ -378,16 +406,18 @@ Variable* Scope::Lookup(Handle<String> name) { Variable* Scope::DeclareFunctionVar(Handle<String> name) { ASSERT(is_function_scope() && function_ == NULL); - function_ = new Variable(this, name, Variable::CONST, true, Variable::NORMAL); - return function_; + Variable* function_var = + new Variable(this, name, Variable::CONST, true, Variable::NORMAL); + function_ = new(isolate_->zone()) VariableProxy(isolate_, function_var); + return function_var; } -void Scope::DeclareParameter(Handle<String> name) { +void Scope::DeclareParameter(Handle<String> name, Variable::Mode mode) { ASSERT(!already_resolved()); ASSERT(is_function_scope()); Variable* var = - variables_.Declare(this, name, Variable::VAR, true, Variable::NORMAL); + variables_.Declare(this, name, mode, true, Variable::NORMAL); params_.Add(var); } @@ -407,7 +437,8 @@ Variable* Scope::DeclareLocal(Handle<String> name, Variable::Mode mode) { Variable* Scope::DeclareGlobal(Handle<String> name) { ASSERT(is_global_scope()); - return variables_.Declare(this, name, Variable::DYNAMIC_GLOBAL, true, + return variables_.Declare(this, name, Variable::DYNAMIC_GLOBAL, + true, Variable::NORMAL); } @@ -440,8 +471,11 @@ void Scope::RemoveUnresolved(VariableProxy* var) { Variable* Scope::NewTemporary(Handle<String> name) { ASSERT(!already_resolved()); - Variable* var = - new Variable(this, name, Variable::TEMPORARY, true, Variable::NORMAL); + Variable* var = new Variable(this, + name, + Variable::TEMPORARY, + true, + Variable::NORMAL); temps_.Add(var); return var; } @@ -467,6 +501,28 @@ void Scope::VisitIllegalRedeclaration(AstVisitor* visitor) { } +Declaration* Scope::CheckConflictingVarDeclarations() { + int length = decls_.length(); + for (int i = 0; i < length; i++) { + Declaration* decl = decls_[i]; + if (decl->mode() != Variable::VAR) continue; + Handle<String> name = decl->proxy()->name(); + bool cond = true; + for (Scope* scope = decl->scope(); cond ; scope = scope->outer_scope_) { + // There is a conflict if there exists a non-VAR binding. + Variable* other_var = scope->variables_.Lookup(name); + if (other_var != NULL && other_var->mode() != Variable::VAR) { + return decl; + } + + // Include declaration scope in the iteration but stop after. + if (!scope->is_block_scope() && !scope->is_catch_scope()) cond = false; + } + } + return NULL; +} + + template<class Allocator> void Scope::CollectUsedVariables(List<Variable*, Allocator>* locals) { // Collect variables in this scope. @@ -607,22 +663,40 @@ static void Indent(int n, const char* str) { static void PrintName(Handle<String> name) { - SmartPointer<char> s = name->ToCString(DISALLOW_NULLS); + SmartArrayPointer<char> s = name->ToCString(DISALLOW_NULLS); PrintF("%s", *s); } -static void PrintVar(PrettyPrinter* printer, int indent, Variable* var) { - if (var->is_used() || var->rewrite() != NULL) { +static void PrintLocation(Variable* var) { + switch (var->location()) { + case Variable::UNALLOCATED: + break; + case Variable::PARAMETER: + PrintF("parameter[%d]", var->index()); + break; + case Variable::LOCAL: + PrintF("local[%d]", var->index()); + break; + case Variable::CONTEXT: + PrintF("context[%d]", var->index()); + break; + case Variable::LOOKUP: + PrintF("lookup"); + break; + } +} + + +static void PrintVar(int indent, Variable* var) { + if (var->is_used() || !var->IsUnallocated()) { Indent(indent, Variable::Mode2String(var->mode())); PrintF(" "); PrintName(var->name()); PrintF("; // "); - if (var->rewrite() != NULL) { - PrintF("%s, ", printer->Print(var->rewrite())); - if (var->is_accessed_from_inner_scope()) PrintF(", "); - } + PrintLocation(var); if (var->is_accessed_from_inner_scope()) { + if (!var->IsUnallocated()) PrintF(", "); PrintF("inner scope access"); } PrintF("\n"); @@ -630,10 +704,10 @@ static void PrintVar(PrettyPrinter* printer, int indent, Variable* var) { } -static void PrintMap(PrettyPrinter* printer, int indent, VariableMap* map) { +static void PrintMap(int indent, VariableMap* map) { for (VariableMap::Entry* p = map->Start(); p != NULL; p = map->Next(p)) { Variable* var = reinterpret_cast<Variable*>(p->value); - PrintVar(printer, indent, var); + PrintVar(indent, var); } } @@ -690,25 +764,24 @@ void Scope::Print(int n) { PrintF("%d heap slots\n", num_heap_slots_); } // Print locals. - PrettyPrinter printer; Indent(n1, "// function var\n"); if (function_ != NULL) { - PrintVar(&printer, n1, function_); + PrintVar(n1, function_->var()); } Indent(n1, "// temporary vars\n"); for (int i = 0; i < temps_.length(); i++) { - PrintVar(&printer, n1, temps_[i]); + PrintVar(n1, temps_[i]); } Indent(n1, "// local vars\n"); - PrintMap(&printer, n1, &variables_); + PrintMap(n1, &variables_); Indent(n1, "// dynamic vars\n"); if (dynamics_ != NULL) { - PrintMap(&printer, n1, dynamics_->GetMap(Variable::DYNAMIC)); - PrintMap(&printer, n1, dynamics_->GetMap(Variable::DYNAMIC_LOCAL)); - PrintMap(&printer, n1, dynamics_->GetMap(Variable::DYNAMIC_GLOBAL)); + PrintMap(n1, dynamics_->GetMap(Variable::DYNAMIC)); + PrintMap(n1, dynamics_->GetMap(Variable::DYNAMIC_LOCAL)); + PrintMap(n1, dynamics_->GetMap(Variable::DYNAMIC_GLOBAL)); } // Print inner scopes (disable by providing negative n). @@ -732,7 +805,7 @@ Variable* Scope::NonLocal(Handle<String> name, Variable::Mode mode) { // Declare a new non-local. var = map->Declare(NULL, name, mode, true, Variable::NORMAL); // Allocate it by giving it a dynamic lookup. - var->set_rewrite(NewSlot(var, Slot::LOOKUP, -1)); + var->AllocateTo(Variable::LOOKUP, -1); } return var; } @@ -774,7 +847,7 @@ Variable* Scope::LookupRecursive(Handle<String> name, // the name of named function literal is kept in an intermediate scope // in between this scope and the next outer scope.) if (function_ != NULL && function_->name().is_identical_to(name)) { - var = function_; + var = function_->var(); } else if (outer_scope_ != NULL) { var = outer_scope_->LookupRecursive(name, true, invalidated_local); @@ -989,12 +1062,12 @@ bool Scope::HasArgumentsParameter() { void Scope::AllocateStackSlot(Variable* var) { - var->set_rewrite(NewSlot(var, Slot::LOCAL, num_stack_slots_++)); + var->AllocateTo(Variable::LOCAL, num_stack_slots_++); } void Scope::AllocateHeapSlot(Variable* var) { - var->set_rewrite(NewSlot(var, Slot::CONTEXT, num_heap_slots_++)); + var->AllocateTo(Variable::CONTEXT, num_heap_slots_++); } @@ -1040,14 +1113,14 @@ void Scope::AllocateParameterLocals() { if (MustAllocate(var)) { if (MustAllocateInContext(var)) { - ASSERT(var->rewrite() == NULL || var->IsContextSlot()); - if (var->rewrite() == NULL) { + ASSERT(var->IsUnallocated() || var->IsContextSlot()); + if (var->IsUnallocated()) { AllocateHeapSlot(var); } } else { - ASSERT(var->rewrite() == NULL || var->IsParameter()); - if (var->rewrite() == NULL) { - var->set_rewrite(NewSlot(var, Slot::PARAMETER, i)); + ASSERT(var->IsUnallocated() || var->IsParameter()); + if (var->IsUnallocated()) { + var->AllocateTo(Variable::PARAMETER, i); } } } @@ -1057,11 +1130,9 @@ void Scope::AllocateParameterLocals() { void Scope::AllocateNonParameterLocal(Variable* var) { ASSERT(var->scope() == this); - ASSERT(var->rewrite() == NULL || - !var->IsVariable(isolate_->factory()->result_symbol()) || - var->AsSlot() == NULL || - var->AsSlot()->type() != Slot::LOCAL); - if (var->rewrite() == NULL && MustAllocate(var)) { + ASSERT(!var->IsVariable(isolate_->factory()->result_symbol()) || + !var->IsStackLocal()); + if (var->IsUnallocated() && MustAllocate(var)) { if (MustAllocateInContext(var)) { AllocateHeapSlot(var); } else { @@ -1089,7 +1160,7 @@ void Scope::AllocateNonParameterLocals() { // because of the current ScopeInfo implementation (see // ScopeInfo::ScopeInfo(FunctionScope* scope) constructor). if (function_ != NULL) { - AllocateNonParameterLocal(function_); + AllocateNonParameterLocal(function_->var()); } } diff --git a/src/scopes.h b/src/scopes.h index c2c41799..2917a63b 100644 --- a/src/scopes.h +++ b/src/scopes.h @@ -112,6 +112,11 @@ class Scope: public ZoneObject { void Initialize(bool inside_with); + // Checks if the block scope is redundant, i.e. it does not contain any + // block scoped declarations. In that case it is removed from the scope + // tree and its children are reparented. + Scope* FinalizeBlockScope(); + // --------------------------------------------------------------------------- // Declarations @@ -130,7 +135,7 @@ class Scope: public ZoneObject { // Declare a parameter in this scope. When there are duplicated // parameters the rightmost one 'wins'. However, the implementation // expects all parameters to be declared and from left to right. - void DeclareParameter(Handle<String> name); + void DeclareParameter(Handle<String> name, Variable::Mode mode); // Declare a local variable in this scope. If the variable has been // declared before, the previously declared variable is returned. @@ -182,6 +187,10 @@ class Scope: public ZoneObject { // Check if the scope has (at least) one illegal redeclaration. bool HasIllegalRedeclaration() const { return illegal_redecl_ != NULL; } + // For harmony block scoping mode: Check if the scope has conflicting var + // declarations, i.e. a var declaration that has been hoisted from a nested + // scope over a let binding of the same name. + Declaration* CheckConflictingVarDeclarations(); // --------------------------------------------------------------------------- // Scope-specific info. @@ -235,7 +244,7 @@ class Scope: public ZoneObject { // The variable holding the function literal for named function // literals, or NULL. // Only valid for function scopes. - Variable* function() const { + VariableProxy* function() const { ASSERT(is_function_scope()); return function_; } @@ -354,7 +363,7 @@ class Scope: public ZoneObject { // Convenience variable. Variable* receiver_; // Function variable, if any; function scopes only. - Variable* function_; + VariableProxy* function_; // Convenience variable; function scopes only. Variable* arguments_; @@ -435,10 +444,6 @@ class Scope: public ZoneObject { // Construct a catch scope with a binding for the name. Scope(Scope* inner_scope, Handle<String> catch_variable_name); - inline Slot* NewSlot(Variable* var, Slot::Type type, int index) { - return new(isolate_->zone()) Slot(isolate_, var, type, index); - } - void AddInnerScope(Scope* inner_scope) { if (inner_scope != NULL) { inner_scopes_.Add(inner_scope); diff --git a/src/serialize.cc b/src/serialize.cc index 094ad20b..ecb480a8 100644 --- a/src/serialize.cc +++ b/src/serialize.cc @@ -240,13 +240,14 @@ void ExternalReferenceTable::PopulateTable(Isolate* isolate) { // Top addresses const char* AddressNames[] = { -#define C(name) "Isolate::" #name, - ISOLATE_ADDRESS_LIST(C) +#define BUILD_NAME_LITERAL(CamelName, hacker_name) \ + "Isolate::" #hacker_name "_address", + FOR_EACH_ISOLATE_ADDRESS_NAME(BUILD_NAME_LITERAL) NULL #undef C }; - for (uint16_t i = 0; i < Isolate::k_isolate_address_count; ++i) { + for (uint16_t i = 0; i < Isolate::kIsolateAddressCount; ++i) { Add(isolate->get_address_from_id((Isolate::AddressId)i), TOP_ADDRESS, i, AddressNames[i]); } diff --git a/src/smart-pointer.h b/src/smart-array-pointer.h index 0fa8224e..00721c1a 100644 --- a/src/smart-pointer.h +++ b/src/smart-array-pointer.h @@ -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: @@ -25,8 +25,8 @@ // (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE // OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -#ifndef V8_SMART_POINTER_H_ -#define V8_SMART_POINTER_H_ +#ifndef V8_SMART_ARRAY_POINTER_H_ +#define V8_SMART_ARRAY_POINTER_H_ namespace v8 { namespace internal { @@ -35,75 +35,66 @@ namespace internal { // A 'scoped array pointer' that calls DeleteArray on its pointer when the // destructor is called. template<typename T> -class SmartPointer { +class SmartArrayPointer { public: + // Default constructor. Constructs an empty scoped pointer. + inline SmartArrayPointer() : p_(NULL) {} - // Default constructor. Construct an empty scoped pointer. - inline SmartPointer() : p(NULL) {} - - - // Construct a scoped pointer from a plain one. - explicit inline SmartPointer(T* pointer) : p(pointer) {} - + // Constructs a scoped pointer from a plain one. + explicit inline SmartArrayPointer(T* ptr) : p_(ptr) {} // Copy constructor removes the pointer from the original to avoid double // freeing. - inline SmartPointer(const SmartPointer<T>& rhs) : p(rhs.p) { - const_cast<SmartPointer<T>&>(rhs).p = NULL; + inline SmartArrayPointer(const SmartArrayPointer<T>& rhs) : p_(rhs.p_) { + const_cast<SmartArrayPointer<T>&>(rhs).p_ = NULL; } - // When the destructor of the scoped pointer is executed the plain pointer // is deleted using DeleteArray. This implies that you must allocate with // NewArray. - inline ~SmartPointer() { if (p) DeleteArray(p); } + inline ~SmartArrayPointer() { if (p_) DeleteArray(p_); } + inline T* operator->() const { return p_; } // You can get the underlying pointer out with the * operator. - inline T* operator*() { return p; } - + inline T* operator*() { return p_; } // You can use [n] to index as if it was a plain pointer inline T& operator[](size_t i) { - return p[i]; + return p_[i]; } // We don't have implicit conversion to a T* since that hinders migration: // You would not be able to change a method from returning a T* to - // returning an SmartPointer<T> and then get errors wherever it is used. + // returning an SmartArrayPointer<T> and then get errors wherever it is used. // If you want to take out the plain pointer and don't want it automatically // deleted then call Detach(). Afterwards, the smart pointer is empty // (NULL). inline T* Detach() { - T* temp = p; - p = NULL; + T* temp = p_; + p_ = NULL; return temp; } - - // Assignment requires an empty (NULL) SmartPointer as the receiver. Like + // Assignment requires an empty (NULL) SmartArrayPointer as the receiver. Like // the copy constructor it removes the pointer in the original to avoid // double freeing. - inline SmartPointer& operator=(const SmartPointer<T>& rhs) { + inline SmartArrayPointer& operator=(const SmartArrayPointer<T>& rhs) { ASSERT(is_empty()); - T* tmp = rhs.p; // swap to handle self-assignment - const_cast<SmartPointer<T>&>(rhs).p = NULL; - p = tmp; + T* tmp = rhs.p_; // swap to handle self-assignment + const_cast<SmartArrayPointer<T>&>(rhs).p_ = NULL; + p_ = tmp; return *this; } - - inline bool is_empty() { - return p == NULL; - } - + inline bool is_empty() { return p_ == NULL; } private: - T* p; + T* p_; }; } } // namespace v8::internal -#endif // V8_SMART_POINTER_H_ +#endif // V8_SMART_ARRAY_POINTER_H_ diff --git a/src/spaces.h b/src/spaces.h index 908cd300..f1564967 100644 --- a/src/spaces.h +++ b/src/spaces.h @@ -1232,8 +1232,8 @@ class PagedSpace : public Space { // Returns the number of total pages in this space. int CountTotalPages(); #endif - private: + private: // Returns a pointer to the page of the relocation pointer. Page* MCRelocationTopPage() { return TopPageOf(mc_forwarding_info_); } @@ -1816,7 +1816,6 @@ class FixedSizeFreeList BASE_EMBEDDED { void MarkNodes(); private: - Heap* heap_; // Available bytes on the free list. diff --git a/src/splay-tree.h b/src/splay-tree.h index 0cb9ea84..72231e4d 100644 --- a/src/splay-tree.h +++ b/src/splay-tree.h @@ -123,8 +123,8 @@ class SplayTree { Value value() { return value_; } Node* left() { return left_; } Node* right() { return right_; } - private: + private: friend class SplayTree; friend class Locator; Key key_; @@ -143,6 +143,7 @@ class SplayTree { Value& value() { return node_->value_; } void set_value(const Value& value) { node_->value_ = value; } inline void bind(Node* node) { node_ = node; } + private: Node* node_; }; @@ -151,7 +152,6 @@ class SplayTree { void ForEach(Callback* callback); protected: - // Resets tree root. Existing nodes become unreachable. void ResetRoot() { root_ = NULL; } @@ -187,7 +187,6 @@ class SplayTree { void Call(Node* node) { delete node; } private: - DISALLOW_COPY_AND_ASSIGN(NodeDeleter); }; diff --git a/src/string-stream.cc b/src/string-stream.cc index 9002593b..8086cf95 100644 --- a/src/string-stream.cc +++ b/src/string-stream.cc @@ -252,11 +252,11 @@ void StringStream::Add(const char* format, FmtElm arg0, FmtElm arg1, } -SmartPointer<const char> StringStream::ToCString() const { +SmartArrayPointer<const char> StringStream::ToCString() const { char* str = NewArray<char>(length_ + 1); memcpy(str, buffer_, length_); str[length_] = '\0'; - return SmartPointer<const char>(str); + return SmartArrayPointer<const char>(str); } diff --git a/src/string-stream.h b/src/string-stream.h index b3f2e0d7..0ba8f52d 100644 --- a/src/string-stream.h +++ b/src/string-stream.h @@ -93,6 +93,7 @@ class FmtElm { FmtElm(void* value) : type_(POINTER) { // NOLINT data_.u_pointer_ = value; } + private: friend class StringStream; enum Type { INT, DOUBLE, C_STR, LC_STR, OBJ, HANDLE, POINTER }; @@ -142,7 +143,7 @@ class StringStream { void OutputToStdOut() { OutputToFile(stdout); } void Log(); Handle<String> ToString(); - SmartPointer<const char> ToCString() const; + SmartArrayPointer<const char> ToCString() const; int length() const { return length_; } // Object printing support. diff --git a/src/string.js b/src/string.js index a70eeade..297105d0 100644 --- a/src/string.js +++ b/src/string.js @@ -223,7 +223,7 @@ function StringReplace(search, replace) { // 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 @@ function StringReplace(search, replace) { 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 @@ function StringReplaceGlobalRegExpWithFunction(subject, regexp, replace) { 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 @@ function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { // 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 @@ function StringReplaceNonGlobalRegExpWithFunction(subject, regexp, replace) { 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. @@ -911,50 +912,47 @@ function ReplaceResultBuilder(str) { this.special_string = str; } -ReplaceResultBuilder.prototype.__proto__ = null; - - -ReplaceResultBuilder.prototype.add = function(str) { - str = TO_STRING_INLINE(str); - if (str.length > 0) this.elements.push(str); -} - - -ReplaceResultBuilder.prototype.addSpecialSlice = function(start, end) { - var len = end - start; - if (start < 0 || len <= 0) return; - if (start < 0x80000 && len < 0x800) { - this.elements.push((start << 11) | len); - } else { - // 0 < len <= String::kMaxLength and Smi::kMaxValue >= String::kMaxLength, - // so -len is a smi. +SetUpLockedPrototype(ReplaceResultBuilder, + $Array("elements", "special_string"), $Array( + "add", function(str) { + str = TO_STRING_INLINE(str); + if (str.length > 0) this.elements.push(str); + }, + "addSpecialSlice", function(start, end) { + var len = end - start; + if (start < 0 || len <= 0) return; + if (start < 0x80000 && len < 0x800) { + this.elements.push((start << 11) | len); + } else { + // 0 < len <= String::kMaxLength and Smi::kMaxValue >= String::kMaxLength, + // so -len is a smi. + var elements = this.elements; + elements.push(-len); + elements.push(start); + } + }, + "generate", function() { var elements = this.elements; - elements.push(-len); - elements.push(start); + return %StringBuilderConcat(elements, elements.length, this.special_string); } -} - - -ReplaceResultBuilder.prototype.generate = function() { - var elements = this.elements; - return %StringBuilderConcat(elements, elements.length, this.special_string); -} +)); // ------------------------------------------------------------------- -function SetupString() { - // Setup the constructor property on the String prototype object. +function SetUpString() { + %CheckIsBootstrapping(); + // Set up the constructor property on the String prototype object. %SetProperty($String.prototype, "constructor", $String, DONT_ENUM); - // Setup the non-enumerable functions on the String object. + // Set up the non-enumerable functions on the String object. InstallFunctions($String, DONT_ENUM, $Array( "fromCharCode", StringFromCharCode )); - // Setup the non-enumerable functions on the String prototype object. + // Set up the non-enumerable functions on the String prototype object. InstallFunctionsOnHiddenPrototype($String.prototype, DONT_ENUM, $Array( "valueOf", StringValueOf, "toString", StringToString, @@ -994,5 +992,4 @@ function SetupString() { )); } - -SetupString(); +SetUpString(); diff --git a/src/strtod.cc b/src/strtod.cc index 568531ef..c89c8f33 100644 --- a/src/strtod.cc +++ b/src/strtod.cc @@ -27,8 +27,9 @@ #include <stdarg.h> #include <math.h> +#include <limits> -#include "platform.h" +#include "globals.h" #include "utils.h" #include "strtod.h" #include "bignum.h" diff --git a/src/stub-cache.cc b/src/stub-cache.cc index 13b0b633..55963303 100644 --- a/src/stub-cache.cc +++ b/src/stub-cache.cc @@ -74,7 +74,7 @@ Code* StubCache::Set(String* name, Map* map, Code* code) { // the bits are the least significant so they will be the ones // masked out. ASSERT(Code::ExtractICStateFromFlags(flags) == MONOMORPHIC); - ASSERT(Code::kFlagsICStateShift == 0); + STATIC_ASSERT((Code::ICStateField::kMask & 1) == 1); // Make sure that the code type is not included in the hash. ASSERT(Code::ExtractTypeFromFlags(flags) == 0); @@ -652,7 +652,6 @@ MaybeObject* StubCache::ComputeKeyedStoreField(String* name, (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 @@ MaybeObject* StubCache::ComputeCallConstant(int argc, 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 @@ MaybeObject* StubCache::ComputeCallConstant(int argc, // 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::ComputeCallConstant(int argc, MaybeObject* StubCache::ComputeCallField(int argc, - InLoopFlag in_loop, Code::Kind kind, Code::ExtraICState extra_ic_state, String* name, @@ -734,12 +730,10 @@ MaybeObject* StubCache::ComputeCallField(int argc, 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 @@ MaybeObject* StubCache::ComputeCallInterceptor( 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::ComputeCallInterceptor( 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::ComputeCallNormal(int argc, MaybeObject* StubCache::ComputeCallGlobal(int argc, - InLoopFlag in_loop, Code::Kind kind, Code::ExtraICState extra_ic_state, String* name, @@ -841,7 +830,6 @@ MaybeObject* StubCache::ComputeCallGlobal(int argc, NORMAL, extra_ic_state, cache_holder, - in_loop, argc); Object* code = map_holder->map()->FindInCodeCache(name, flags); if (code->IsUndefined()) { @@ -850,8 +838,7 @@ MaybeObject* StubCache::ComputeCallGlobal(int argc, // 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 @@ static MaybeObject* FillCache(Isolate* isolate, MaybeObject* maybe_code) { 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 @@ Code* StubCache::FindCallInitialize(int argc, 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 @@ MaybeObject* StubCache::ComputeCallInitialize(int argc, 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::ComputeCallPreMonomorphic( 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::ComputeCallNormal(int argc, } -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::ComputeCallArguments(int argc, 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 @@ MaybeObject* StubCache::ComputeCallMiss(int argc, // 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 @@ MaybeObject* StubCache::ComputeCallDebugBreak( // 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 @@ MaybeObject* StubCache::ComputeCallDebugPrepareStepIn( // 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 @@ MaybeObject* KeyedLoadStubCompiler::GetCode(PropertyType type, 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* KeyedLoadStubCompiler::GetCode(PropertyType type, 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* StoreStubCompiler::GetCode(PropertyType type, String* name) { 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 @@ void KeyedStoreStubCompiler::GenerateStoreDictionaryElement( 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 @@ MaybeObject* CallStubCompiler::GetCode(PropertyType type, String* name) { 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 dd06a1c0..18c157b1 100644 --- a/src/stub-cache.h +++ b/src/stub-cache.h @@ -194,7 +194,6 @@ class StubCache { MUST_USE_RESULT MaybeObject* ComputeCallField( int argc, - InLoopFlag in_loop, Code::Kind, Code::ExtraICState extra_ic_state, String* name, @@ -204,7 +203,6 @@ class StubCache { MUST_USE_RESULT MaybeObject* ComputeCallConstant( int argc, - InLoopFlag in_loop, Code::Kind, Code::ExtraICState extra_ic_state, String* name, @@ -214,7 +212,6 @@ class StubCache { MUST_USE_RESULT MaybeObject* ComputeCallNormal( int argc, - InLoopFlag in_loop, Code::Kind, Code::ExtraICState extra_ic_state, String* name, @@ -230,7 +227,6 @@ class StubCache { MUST_USE_RESULT MaybeObject* ComputeCallGlobal( int argc, - InLoopFlag in_loop, Code::Kind, Code::ExtraICState extra_ic_state, String* name, @@ -242,33 +238,27 @@ class StubCache { // --- 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 @@ class StubCache { // 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 @@ class StubCache { // 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::kFlagsICInLoopMask); - uint32_t key = seed - string_low32bits + iflags; + uint32_t key = seed - string_low32bits + flags; return key & ((kSecondaryTableSize - 1) << kHeapObjectTagSize); } @@ -660,7 +645,7 @@ class KeyedLoadStubCompiler: public StubCompiler { CodeList* handler_ics); static void GenerateLoadExternalArray(MacroAssembler* masm, - JSObject::ElementsKind elements_kind); + ElementsKind elements_kind); static void GenerateLoadFastElement(MacroAssembler* masm); @@ -725,7 +710,7 @@ class KeyedStoreStubCompiler: public StubCompiler { bool is_js_array); static void GenerateStoreExternalArray(MacroAssembler* masm, - JSObject::ElementsKind elements_kind); + ElementsKind elements_kind); static void GenerateStoreDictionaryElement(MacroAssembler* masm); @@ -755,7 +740,6 @@ class CallOptimization; 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 @@ class CallStubCompiler: public StubCompiler { 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 bdf7bc3c..c64368e5 100644 --- a/src/type-info.cc +++ b/src/type-info.cc @@ -190,7 +190,6 @@ void TypeFeedbackOracle::CallReceiverTypes(Call* expr, NORMAL, extra_ic_state, OWN_MAP, - NOT_IN_LOOP, arity); CollectReceiverTypes(expr->id(), name, flags, types); } @@ -392,8 +392,9 @@ function URIUnescape(str) { // ------------------------------------------------------------------- -function SetupURI() { - // Setup non-enumerable URI functions on the global object and set +function SetUpUri() { + %CheckIsBootstrapping(); + // Set up non-enumerable URI functions on the global object and set // their names. InstallFunctions(global, DONT_ENUM, $Array( "escape", URIEscape, @@ -405,4 +406,4 @@ function SetupURI() { )); } -SetupURI(); +SetUpUri(); diff --git a/src/utils.h b/src/utils.h index 785bc437..26c522b8 100644 --- a/src/utils.h +++ b/src/utils.h @@ -203,16 +203,17 @@ inline int StrLength(const char* string) { template<class T, int shift, int size> class BitField { public: + // A uint32_t mask of bit field. To use all bits of a uint32 in a + // bitfield without compiler warnings we have to compute 2^32 without + // using a shift count of 32. + static const uint32_t kMask = ((1U << shift) << size) - (1U << shift); + + // Value for the field with all bits set. + static const T kMax = static_cast<T>((1U << size) - 1); + // Tells whether the provided value fits into the bit field. static bool is_valid(T value) { - return (static_cast<uint32_t>(value) & ~((1U << (size)) - 1)) == 0; - } - - // Returns a uint32_t mask of bit field. - static uint32_t mask() { - // To use all bits of a uint32 in a bitfield without compiler warnings we - // have to compute 2^32 without using a shift count of 32. - return ((1U << shift) << size) - (1U << shift); + return (static_cast<uint32_t>(value) & ~static_cast<uint32_t>(kMax)) == 0; } // Returns a uint32_t with the bit field value encoded. @@ -223,17 +224,12 @@ class BitField { // Returns a uint32_t with the bit field value updated. static uint32_t update(uint32_t previous, T value) { - return (previous & ~mask()) | encode(value); + return (previous & ~kMask) | encode(value); } // Extracts the bit field from the value. static T decode(uint32_t value) { - return static_cast<T>((value & mask()) >> shift); - } - - // Value for the field with all bits set. - static T max() { - return decode(mask()); + return static_cast<T>((value & kMask) >> shift); } }; @@ -497,9 +493,6 @@ class Collector { public: explicit Collector(int initial_capacity = kMinCapacity) : index_(0), size_(0) { - if (initial_capacity < kMinCapacity) { - initial_capacity = kMinCapacity; - } current_chunk_ = Vector<T>::New(initial_capacity); } @@ -601,25 +594,23 @@ class Collector { // Creates a new current chunk, and stores the old chunk in the chunks_ list. void Grow(int min_capacity) { ASSERT(growth_factor > 1); - int growth = current_chunk_.length() * (growth_factor - 1); - if (growth > max_growth) { - growth = max_growth; - } - int new_capacity = current_chunk_.length() + growth; - if (new_capacity < min_capacity) { - new_capacity = min_capacity + growth; - } - Vector<T> new_chunk = Vector<T>::New(new_capacity); - int new_index = PrepareGrow(new_chunk); - if (index_ > 0) { - chunks_.Add(current_chunk_.SubVector(0, index_)); + int new_capacity; + int current_length = current_chunk_.length(); + if (current_length < kMinCapacity) { + // The collector started out as empty. + new_capacity = min_capacity * growth_factor; + if (new_capacity < kMinCapacity) new_capacity = kMinCapacity; } else { - // Can happen if the call to PrepareGrow moves everything into - // the new chunk. - current_chunk_.Dispose(); + int growth = current_length * (growth_factor - 1); + if (growth > max_growth) { + growth = max_growth; + } + new_capacity = current_length + growth; + if (new_capacity < min_capacity) { + new_capacity = min_capacity + growth; + } } - current_chunk_ = new_chunk; - index_ = new_index; + NewChunk(new_capacity); ASSERT(index_ + min_capacity <= current_chunk_.length()); } @@ -627,8 +618,15 @@ class Collector { // some of the current data into the new chunk. The function may update // the current index_ value to represent data no longer in the current chunk. // Returns the initial index of the new chunk (after copied data). - virtual int PrepareGrow(Vector<T> new_chunk) { - return 0; + virtual void NewChunk(int new_capacity) { + Vector<T> new_chunk = Vector<T>::New(new_capacity); + if (index_ > 0) { + chunks_.Add(current_chunk_.SubVector(0, index_)); + } else { + current_chunk_.Dispose(); + } + current_chunk_ = new_chunk; + index_ = 0; } }; @@ -683,20 +681,26 @@ class SequenceCollector : public Collector<T, growth_factor, max_growth> { int sequence_start_; // Move the currently active sequence to the new chunk. - virtual int PrepareGrow(Vector<T> new_chunk) { - if (sequence_start_ != kNoSequence) { - int sequence_length = this->index_ - sequence_start_; - // The new chunk is always larger than the current chunk, so there - // is room for the copy. - ASSERT(sequence_length < new_chunk.length()); - for (int i = 0; i < sequence_length; i++) { - new_chunk[i] = this->current_chunk_[sequence_start_ + i]; - } - this->index_ = sequence_start_; - sequence_start_ = 0; - return sequence_length; + virtual void NewChunk(int new_capacity) { + if (sequence_start_ == kNoSequence) { + // Fall back on default behavior if no sequence has been started. + this->Collector<T, growth_factor, max_growth>::NewChunk(new_capacity); + return; } - return 0; + int sequence_length = this->index_ - sequence_start_; + Vector<T> new_chunk = Vector<T>::New(sequence_length + new_capacity); + ASSERT(sequence_length < new_chunk.length()); + for (int i = 0; i < sequence_length; i++) { + new_chunk[i] = this->current_chunk_[sequence_start_ + i]; + } + if (sequence_start_ > 0) { + this->chunks_.Add(this->current_chunk_.SubVector(0, sequence_start_)); + } else { + this->current_chunk_.Dispose(); + } + this->current_chunk_ = new_chunk; + this->index_ = sequence_length; + sequence_start_ = 0; } }; @@ -882,6 +886,7 @@ class SimpleStringBuilder { int position_; bool is_finalized() const { return position_ < 0; } + private: DISALLOW_IMPLICIT_CONSTRUCTORS(SimpleStringBuilder); }; diff --git a/src/v8conversions.cc b/src/v8conversions.cc index 96056ecf..bf175e50 100644 --- a/src/v8conversions.cc +++ b/src/v8conversions.cc @@ -34,7 +34,6 @@ #include "v8conversions.h" #include "dtoa.h" #include "factory.h" -#include "scanner-base.h" #include "strtod.h" namespace v8 { diff --git a/src/v8globals.h b/src/v8globals.h index f05a702e..eb5c49d7 100644 --- a/src/v8globals.h +++ b/src/v8globals.h @@ -98,6 +98,10 @@ const int kPageSizeBits = 13; const int kProcessorCacheLineSize = 64; // Constants relevant to double precision floating point numbers. + +// Quiet NaNs have bits 51 to 62 set, possibly the sign bit, and no +// other bits set. +const uint64_t kQuietNaNMask = static_cast<uint64_t>(0xfff) << 51; // If looking only at the top 32 bits, the QNaN mask is bits 19 to 30. const uint32_t kQuietNaNHighBitsMask = 0xfff << (51 - 32); @@ -298,12 +302,6 @@ enum CheckType { }; -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 @@ -330,7 +328,7 @@ enum PropertyType { HANDLER = 4, // only in lookup results, not in descriptors INTERCEPTOR = 5, // only in lookup results, not in descriptors MAP_TRANSITION = 6, // only in fast mode - EXTERNAL_ARRAY_TRANSITION = 7, + ELEMENTS_TRANSITION = 7, CONSTANT_TRANSITION = 8, // only in fast mode NULL_DESCRIPTOR = 9, // only in fast mode // All properties before MAP_TRANSITION are real. diff --git a/src/v8natives.js b/src/v8natives.js index 982e18e4..588bdb21 100644 --- a/src/v8natives.js +++ b/src/v8natives.js @@ -41,7 +41,6 @@ const $isNaN = GlobalIsNaN; const $isFinite = GlobalIsFinite; - // ---------------------------------------------------------------------------- @@ -66,28 +65,56 @@ function InstallFunctions(object, attributes, functions) { // functions on String.prototype etc. and then restore the old function // with delete. See http://code.google.com/p/chromium/issues/detail?id=1717 function InstallFunctionsOnHiddenPrototype(object, attributes, functions) { + %CheckIsBootstrapping(); var hidden_prototype = new $Object(); %SetHiddenPrototype(object, hidden_prototype); InstallFunctions(hidden_prototype, attributes, functions); } +// Prevents changes to the prototype of a built-infunction. +// The "prototype" property of the function object is made non-configurable, +// and the prototype object is made non-extensible. The latter prevents +// changing the __proto__ property. +function SetUpLockedPrototype(constructor, fields, methods) { + %CheckIsBootstrapping(); + var prototype = constructor.prototype; + // Install functions first, because this function is used to initialize + // PropertyDescriptor itself. + var property_count = (methods.length >> 1) + (fields ? fields.length : 0); + if (property_count >= 4) { + %OptimizeObjectForAddingMultipleProperties(prototype, property_count); + } + if (fields) { + for (var i = 0; i < fields.length; i++) { + %SetProperty(prototype, fields[i], void 0, DONT_ENUM | DONT_DELETE); + } + } + for (var i = 0; i < methods.length; i += 2) { + var key = methods[i]; + var f = methods[i + 1]; + %SetProperty(prototype, key, f, DONT_ENUM | DONT_DELETE | READ_ONLY); + %SetNativeFlag(f); + } + prototype.__proto__ = null; + %ToFastProperties(prototype); +} + + // ---------------------------------------------------------------------------- // ECMA 262 - 15.1.4 function GlobalIsNaN(number) { - var n = ToNumber(number); - return NUMBER_IS_NAN(n); + if (!IS_NUMBER(number)) number = NonNumberToNumber(number); + return NUMBER_IS_NAN(number); } // ECMA 262 - 15.1.5 function GlobalIsFinite(number) { if (!IS_NUMBER(number)) number = NonNumberToNumber(number); - - // NaN - NaN == NaN, Infinity - Infinity == NaN, -Infinity - -Infinity == NaN. - return %_IsSmi(number) || number - number == 0; + return NUMBER_IS_FINITE(number); } @@ -162,8 +189,9 @@ function GlobalEval(x) { // ---------------------------------------------------------------------------- - -function SetupGlobal() { +// Set up global object. +function SetUpGlobal() { + %CheckIsBootstrapping(); // ECMA 262 - 15.1.1.1. %SetProperty(global, "NaN", $NaN, DONT_ENUM | DONT_DELETE); @@ -173,7 +201,7 @@ function SetupGlobal() { // ECMA-262 - 15.1.1.3. %SetProperty(global, "undefined", void 0, DONT_ENUM | DONT_DELETE); - // Setup non-enumerable function on the global object. + // Set up non-enumerable function on the global object. InstallFunctions(global, DONT_ENUM, $Array( "isNaN", GlobalIsNaN, "isFinite", GlobalIsFinite, @@ -183,8 +211,7 @@ function SetupGlobal() { )); } -SetupGlobal(); - +SetUpGlobal(); // ---------------------------------------------------------------------------- // Boolean (first part of definition) @@ -271,7 +298,7 @@ function ObjectDefineGetter(name, fun) { 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(); @@ -296,7 +323,7 @@ function ObjectDefineSetter(name, fun) { 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'); } @@ -426,7 +453,7 @@ function ToPropertyDescriptor(obj) { 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); @@ -434,7 +461,7 @@ function ToPropertyDescriptor(obj) { 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); @@ -481,106 +508,83 @@ function PropertyDescriptor() { this.hasSetter_ = false; } -PropertyDescriptor.prototype.__proto__ = null; - -PropertyDescriptor.prototype.toString = function() { - return "[object PropertyDescriptor]"; -}; - -PropertyDescriptor.prototype.setValue = function(value) { - this.value_ = value; - this.hasValue_ = true; -} - - -PropertyDescriptor.prototype.getValue = function() { - return this.value_; -} - - -PropertyDescriptor.prototype.hasValue = function() { - return this.hasValue_; -} - - -PropertyDescriptor.prototype.setEnumerable = function(enumerable) { - this.enumerable_ = enumerable; - this.hasEnumerable_ = true; -} - - -PropertyDescriptor.prototype.isEnumerable = function () { - return this.enumerable_; -} - - -PropertyDescriptor.prototype.hasEnumerable = function() { - return this.hasEnumerable_; -} - - -PropertyDescriptor.prototype.setWritable = function(writable) { - this.writable_ = writable; - this.hasWritable_ = true; -} - - -PropertyDescriptor.prototype.isWritable = function() { - return this.writable_; -} - - -PropertyDescriptor.prototype.hasWritable = function() { - return this.hasWritable_; -} - - -PropertyDescriptor.prototype.setConfigurable = function(configurable) { - this.configurable_ = configurable; - this.hasConfigurable_ = true; -} - - -PropertyDescriptor.prototype.hasConfigurable = function() { - return this.hasConfigurable_; -} - - -PropertyDescriptor.prototype.isConfigurable = function() { - return this.configurable_; -} - - -PropertyDescriptor.prototype.setGet = function(get) { - this.get_ = get; - this.hasGetter_ = true; -} - - -PropertyDescriptor.prototype.getGet = function() { - return this.get_; -} - - -PropertyDescriptor.prototype.hasGetter = function() { - return this.hasGetter_; -} - - -PropertyDescriptor.prototype.setSet = function(set) { - this.set_ = set; - this.hasSetter_ = true; -} - - -PropertyDescriptor.prototype.getSet = function() { - return this.set_; -} - - -PropertyDescriptor.prototype.hasSetter = function() { - return this.hasSetter_; -} +SetUpLockedPrototype(PropertyDescriptor, $Array( + "value_", + "hasValue_", + "writable_", + "hasWritable_", + "enumerable_", + "hasEnumerable_", + "configurable_", + "hasConfigurable_", + "get_", + "hasGetter_", + "set_", + "hasSetter_" + ), $Array( + "toString", function() { + return "[object PropertyDescriptor]"; + }, + "setValue", function(value) { + this.value_ = value; + this.hasValue_ = true; + }, + "getValue", function() { + return this.value_; + }, + "hasValue", function() { + return this.hasValue_; + }, + "setEnumerable", function(enumerable) { + this.enumerable_ = enumerable; + this.hasEnumerable_ = true; + }, + "isEnumerable", function () { + return this.enumerable_; + }, + "hasEnumerable", function() { + return this.hasEnumerable_; + }, + "setWritable", function(writable) { + this.writable_ = writable; + this.hasWritable_ = true; + }, + "isWritable", function() { + return this.writable_; + }, + "hasWritable", function() { + return this.hasWritable_; + }, + "setConfigurable", function(configurable) { + this.configurable_ = configurable; + this.hasConfigurable_ = true; + }, + "hasConfigurable", function() { + return this.hasConfigurable_; + }, + "isConfigurable", function() { + return this.configurable_; + }, + "setGet", function(get) { + this.get_ = get; + this.hasGetter_ = true; + }, + "getGet", function() { + return this.get_; + }, + "hasGetter", function() { + return this.hasGetter_; + }, + "setSet", function(set) { + this.set_ = set; + this.hasSetter_ = true; + }, + "getSet", function() { + return this.set_; + }, + "hasSetter", function() { + return this.hasSetter_; + })); // Converts an array returned from Runtime_GetOwnProperty to an actual @@ -619,7 +623,7 @@ function GetTrap(handler, name, defaultTrap) { 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; @@ -973,7 +977,7 @@ function ObjectDefineProperty(obj, p, attributes) { // 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]; } @@ -1037,7 +1041,16 @@ function ProxyFix(obj) { 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); } @@ -1168,10 +1181,11 @@ function ObjectIsExtensible(obj) { %SetExpectedNumberOfProperties($Object, 4); // ---------------------------------------------------------------------------- +// Object - -function SetupObject() { - // Setup non-enumerable functions on the Object.prototype object. +function SetUpObject() { + %CheckIsBootstrapping(); + // Set Up non-enumerable functions on the Object.prototype object. InstallFunctions($Object.prototype, DONT_ENUM, $Array( "toString", ObjectToString, "toLocaleString", ObjectToLocaleString, @@ -1201,8 +1215,7 @@ function SetupObject() { )); } -SetupObject(); - +SetUpObject(); // ---------------------------------------------------------------------------- // Boolean @@ -1233,14 +1246,16 @@ function BooleanValueOf() { // ---------------------------------------------------------------------------- -function SetupBoolean() { +function SetUpBoolean () { + %CheckIsBootstrapping(); InstallFunctions($Boolean.prototype, DONT_ENUM, $Array( "toString", BooleanToString, "valueOf", BooleanValueOf )); } -SetupBoolean(); +SetUpBoolean(); + // ---------------------------------------------------------------------------- // Number @@ -1354,9 +1369,10 @@ function NumberToPrecision(precision) { // ---------------------------------------------------------------------------- -function SetupNumber() { +function SetUpNumber() { + %CheckIsBootstrapping(); %OptimizeObjectForAddingMultipleProperties($Number.prototype, 8); - // Setup the constructor property on the Number prototype object. + // Set up the constructor property on the Number prototype object. %SetProperty($Number.prototype, "constructor", $Number, DONT_ENUM); %OptimizeObjectForAddingMultipleProperties($Number, 5); @@ -1385,7 +1401,7 @@ function SetupNumber() { DONT_ENUM | DONT_DELETE | READ_ONLY); %ToFastProperties($Number); - // Setup non-enumerable functions on the Number prototype object. + // Set up non-enumerable functions on the Number prototype object. InstallFunctions($Number.prototype, DONT_ENUM, $Array( "toString", NumberToString, "toLocaleString", NumberToLocaleString, @@ -1396,7 +1412,7 @@ function SetupNumber() { )); } -SetupNumber(); +SetUpNumber(); // ---------------------------------------------------------------------------- @@ -1405,6 +1421,10 @@ SetupNumber(); $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'); } @@ -1434,12 +1454,13 @@ function FunctionToString() { // 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()) { @@ -1448,8 +1469,7 @@ function FunctionBind(this_arg) { // Length is 1. // 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); @@ -1479,7 +1499,7 @@ function FunctionBind(this_arg) { // Length is 1. 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); }; } @@ -1490,11 +1510,16 @@ function FunctionBind(this_arg) { // Length is 1. // 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; } @@ -1525,11 +1550,12 @@ function NewFunction(arg1) { // length == 1 // ---------------------------------------------------------------------------- -function SetupFunction() { +function SetUpFunction() { + %CheckIsBootstrapping(); InstallFunctions($Function.prototype, DONT_ENUM, $Array( "bind", FunctionBind, "toString", FunctionToString )); } -SetupFunction(); +SetUpFunction(); diff --git a/src/v8threads.h b/src/v8threads.h index 3ba823a7..4002bb36 100644 --- a/src/v8threads.h +++ b/src/v8threads.h @@ -54,6 +54,7 @@ class ThreadState { // Get data area for archiving a thread. char* data() { return data_; } + private: explicit ThreadState(ThreadManager* thread_manager); diff --git a/src/variables.cc b/src/variables.cc index e82e6741..971061b0 100644 --- a/src/variables.cc +++ b/src/variables.cc @@ -53,34 +53,6 @@ const char* Variable::Mode2String(Mode mode) { } -Property* Variable::AsProperty() const { - return rewrite_ == NULL ? NULL : rewrite_->AsProperty(); -} - - -Slot* Variable::AsSlot() const { return rewrite_; } - - -bool Variable::IsStackAllocated() const { - return rewrite_ != NULL && rewrite_->IsStackAllocated(); -} - - -bool Variable::IsParameter() const { - return rewrite_ != NULL && rewrite_->type() == Slot::PARAMETER; -} - - -bool Variable::IsStackLocal() const { - return rewrite_ != NULL && rewrite_->type() == Slot::LOCAL; -} - - -bool Variable::IsContextSlot() const { - return rewrite_ != NULL && rewrite_->type() == Slot::CONTEXT; -} - - Variable::Variable(Scope* scope, Handle<String> name, Mode mode, @@ -90,8 +62,9 @@ Variable::Variable(Scope* scope, name_(name), mode_(mode), kind_(kind), + location_(UNALLOCATED), + index_(-1), local_if_not_shadowed_(NULL), - rewrite_(NULL), is_valid_LHS_(is_valid_LHS), is_accessed_from_inner_scope_(false), is_used_(false) { diff --git a/src/variables.h b/src/variables.h index 2095555a..56c8dabd 100644 --- a/src/variables.h +++ b/src/variables.h @@ -74,6 +74,33 @@ class Variable: public ZoneObject { ARGUMENTS }; + enum Location { + // Before and during variable allocation, a variable whose location is + // not yet determined. After allocation, a variable looked up as a + // property on the global object (and possibly absent). name() is the + // variable name, index() is invalid. + UNALLOCATED, + + // A slot in the parameter section on the stack. index() is the + // parameter index, counting left-to-right. The reciever is index -1; + // the first parameter is index 0. + PARAMETER, + + // A slot in the local section on the stack. index() is the variable + // index in the stack frame, starting at 0. + LOCAL, + + // An indexed slot in a heap context. index() is the variable index in + // the context object on the heap, starting at 0. scope() is the + // corresponding scope. + CONTEXT, + + // A named slot in a heap context. name() is the variable name in the + // context object on the heap, with lookup starting at the current + // context. index() is invalid. + LOOKUP + }; + Variable(Scope* scope, Handle<String> name, Mode mode, @@ -83,10 +110,6 @@ class Variable: public ZoneObject { // Printing support static const char* Mode2String(Mode mode); - // Type testing & conversion. Global variables are not slots. - Property* AsProperty() const; - Slot* AsSlot() const; - bool IsValidLeftHandSide() { return is_valid_LHS_; } // The source code for an eval() call may refer to a variable that is @@ -111,10 +134,12 @@ class Variable: public ZoneObject { return !is_this() && name().is_identical_to(n); } - bool IsStackAllocated() const; - bool IsParameter() const; // Includes 'this'. - bool IsStackLocal() const; - bool IsContextSlot() const; + bool IsUnallocated() const { return location_ == UNALLOCATED; } + bool IsParameter() const { return location_ == PARAMETER; } + bool IsStackLocal() const { return location_ == LOCAL; } + bool IsStackAllocated() const { return IsParameter() || IsStackLocal(); } + bool IsContextSlot() const { return location_ == CONTEXT; } + bool IsLookupSlot() const { return location_ == LOOKUP; } bool is_dynamic() const { return (mode_ == DYNAMIC || @@ -141,20 +166,24 @@ class Variable: public ZoneObject { local_if_not_shadowed_ = local; } - Slot* rewrite() const { return rewrite_; } - void set_rewrite(Slot* slot) { rewrite_ = slot; } + Location location() const { return location_; } + int index() const { return index_; } + + void AllocateTo(Location location, int index) { + location_ = location; + index_ = index; + } private: Scope* scope_; Handle<String> name_; Mode mode_; Kind kind_; + Location location_; + int index_; Variable* local_if_not_shadowed_; - // Code generation. - Slot* rewrite_; - // Valid as a LHS? (const and this are not valid LHS, for example) bool is_valid_LHS_; diff --git a/src/version.cc b/src/version.cc index 1a7c7516..7f916e17 100644 --- a/src/version.cc +++ b/src/version.cc @@ -33,9 +33,9 @@ // NOTE these macros are used by the SCons build script so their names // cannot be changed without changing the SCons build script. #define MAJOR_VERSION 3 -#define MINOR_VERSION 5 -#define BUILD_NUMBER 10 -#define PATCH_LEVEL 24 +#define MINOR_VERSION 6 +#define BUILD_NUMBER 6 +#define PATCH_LEVEL 11 // Use 1 for candidates and 0 otherwise. // (Boolean macro values are not supported by all preprocessors.) #define IS_CANDIDATE_VERSION 0 diff --git a/src/weakmap.js b/src/weakmap.js index 3d261e5a..5fb51510 100644 --- a/src/weakmap.js +++ b/src/weakmap.js @@ -80,13 +80,11 @@ function WeakMapDelete(key) { // ------------------------------------------------------------------- -function SetupWeakMap() { +(function () { + %CheckIsBootstrapping(); // 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); @@ -97,7 +95,4 @@ function SetupWeakMap() { "has", WeakMapHas, "delete", WeakMapDelete )); -} - - -SetupWeakMap(); +})(); diff --git a/src/win32-math.cc b/src/win32-math.cc new file mode 100644 index 00000000..3410872b --- /dev/null +++ b/src/win32-math.cc @@ -0,0 +1,106 @@ +// 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: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Extra POSIX/ANSI routines for Win32 when using Visual Studio C++. Please +// refer to The Open Group Base Specification for specification of the correct +// semantics for these functions. +// (http://www.opengroup.org/onlinepubs/000095399/) +#ifdef _MSC_VER + +#undef V8_WIN32_LEAN_AND_MEAN +#define V8_WIN32_HEADERS_FULL +#include "win32-headers.h" +#include <limits.h> // Required for INT_MAX etc. +#include <math.h> +#include <float.h> // Required for DBL_MAX and on Win32 for finite() +#include "win32-math.h" + +#include "checks.h" + +namespace v8 { + +// Test for finite value - usually defined in math.h +int isfinite(double x) { + return _finite(x); +} + +} // namespace v8 + + +// Test for a NaN (not a number) value - usually defined in math.h +int isnan(double x) { + return _isnan(x); +} + + +// Test for infinity - usually defined in math.h +int isinf(double x) { + return (_fpclass(x) & (_FPCLASS_PINF | _FPCLASS_NINF)) != 0; +} + + +// Test if x is less than y and both nominal - usually defined in math.h +int isless(double x, double y) { + return isnan(x) || isnan(y) ? 0 : x < y; +} + + +// Test if x is greater than y and both nominal - usually defined in math.h +int isgreater(double x, double y) { + return isnan(x) || isnan(y) ? 0 : x > y; +} + + +// Classify floating point number - usually defined in math.h +int fpclassify(double x) { + // Use the MS-specific _fpclass() for classification. + int flags = _fpclass(x); + + // Determine class. We cannot use a switch statement because + // the _FPCLASS_ constants are defined as flags. + if (flags & (_FPCLASS_PN | _FPCLASS_NN)) return FP_NORMAL; + if (flags & (_FPCLASS_PZ | _FPCLASS_NZ)) return FP_ZERO; + if (flags & (_FPCLASS_PD | _FPCLASS_ND)) return FP_SUBNORMAL; + if (flags & (_FPCLASS_PINF | _FPCLASS_NINF)) return FP_INFINITE; + + // All cases should be covered by the code above. + ASSERT(flags & (_FPCLASS_SNAN | _FPCLASS_QNAN)); + return FP_NAN; +} + + +// Test sign - usually defined in math.h +int signbit(double x) { + // We need to take care of the special case of both positive + // and negative versions of zero. + if (x == 0) + return _fpclass(x) & _FPCLASS_NZ; + else + return x < 0; +} + +#endif // _MSC_VER diff --git a/src/win32-math.h b/src/win32-math.h new file mode 100644 index 00000000..68759990 --- /dev/null +++ b/src/win32-math.h @@ -0,0 +1,61 @@ +// 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: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following +// disclaimer in the documentation and/or other materials provided +// with the distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived +// from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Extra POSIX/ANSI routines for Win32 when using Visual Studio C++. Please +// refer to The Open Group Base Specification for specification of the correct +// semantics for these functions. +// (http://www.opengroup.org/onlinepubs/000095399/) + +#ifndef V8_WIN32_MATH_H_ +#define V8_WIN32_MATH_H_ + +#ifndef _MSC_VER +#error Wrong environment, expected MSVC. +#endif // _MSC_VER + +enum { + FP_NAN, + FP_INFINITE, + FP_ZERO, + FP_SUBNORMAL, + FP_NORMAL +}; + +namespace v8 { + +int isfinite(double x); + +} // namespace v8 + +int isnan(double x); +int isinf(double x); +int isless(double x, double y); +int isgreater(double x, double y); +int fpclassify(double x); +int signbit(double x); + +#endif // V8_WIN32_MATH_H_ diff --git a/src/x64/assembler-x64.h b/src/x64/assembler-x64.h index c23eb168..2e373faa 100644 --- a/src/x64/assembler-x64.h +++ b/src/x64/assembler-x64.h @@ -453,6 +453,7 @@ class CpuFeatures : public AllStatic { // Enable a specified feature within a scope. class Scope BASE_EMBEDDED { #ifdef DEBUG + public: explicit Scope(CpuFeature f) { uint64_t mask = V8_UINT64_C(1) << f; @@ -472,10 +473,12 @@ class CpuFeatures : public AllStatic { isolate_->set_enabled_cpu_features(old_enabled_); } } + private: Isolate* isolate_; uint64_t old_enabled_; #else + public: explicit Scope(CpuFeature f) {} #endif diff --git a/src/x64/builtins-x64.cc b/src/x64/builtins-x64.cc index 7c6f7e32..db06909d 100644 --- a/src/x64/builtins-x64.cc +++ b/src/x64/builtins-x64.cc @@ -655,15 +655,16 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { // 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 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ 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 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ 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 @@ void Builtins::Generate_FunctionCall(MacroAssembler* masm) { __ 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 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { 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 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ push(rax); // limit __ push(Immediate(0)); // index - // Change context eagerly to get the right global object if - // necessary. + // 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)); - __ movq(rsi, FieldOperand(rdi, JSFunction::kContextOffset)); + __ CmpObjectType(rdi, JS_FUNCTION_TYPE, rcx); + __ j(not_equal, &push_receiver); - // Compute the receiver. - Label call_to_object, use_global_receiver, push_receiver; - __ movq(rbx, Operand(rbp, kReceiverOffset)); + // 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 @@ void Builtins::Generate_FunctionApply(MacroAssembler* masm) { __ 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 9237a0a3..df4438b7 100644 --- a/src/x64/code-stubs-x64.cc +++ b/src/x64/code-stubs-x64.cc @@ -2712,7 +2712,7 @@ void RegExpExecStub::Generate(MacroAssembler* masm) { // haven't created the exception yet. Handle that in the runtime system. // TODO(592): Rerunning the RegExp to get the stack overflow exception. ExternalReference pending_exception_address( - Isolate::k_pending_exception_address, isolate); + Isolate::kPendingExceptionAddress, isolate); Operand pending_exception_operand = masm->ExternalOperand(pending_exception_address, rbx); __ movq(rax, pending_exception_operand); @@ -3232,7 +3232,7 @@ void StackCheckStub::Generate(MacroAssembler* masm) { 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 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { __ 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 @@ void CallFunctionStub::Generate(MacroAssembler* masm) { // 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); } @@ -3428,7 +3445,7 @@ void CEntryStub::GenerateCore(MacroAssembler* masm, // Retrieve the pending exception and clear the variable. ExternalReference pending_exception_address( - Isolate::k_pending_exception_address, masm->isolate()); + Isolate::kPendingExceptionAddress, masm->isolate()); Operand pending_exception_operand = masm->ExternalOperand(pending_exception_address); __ movq(rax, pending_exception_operand); @@ -3568,14 +3585,14 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { Isolate* isolate = masm->isolate(); // Save copies of the top frame descriptor on the stack. - ExternalReference c_entry_fp(Isolate::k_c_entry_fp_address, isolate); + ExternalReference c_entry_fp(Isolate::kCEntryFPAddress, isolate); { Operand c_entry_fp_operand = masm->ExternalOperand(c_entry_fp); __ push(c_entry_fp_operand); } // If this is the outermost JS call, set js_entry_sp value. - ExternalReference js_entry_sp(Isolate::k_js_entry_sp_address, isolate); + ExternalReference js_entry_sp(Isolate::kJSEntrySPAddress, isolate); __ Load(rax, js_entry_sp); __ testq(rax, rax); __ j(not_zero, ¬_outermost_js); @@ -3593,7 +3610,7 @@ void JSEntryStub::GenerateBody(MacroAssembler* masm, bool is_construct) { // Caught exception: Store result (exception) in the pending // exception field in the JSEnv and return a failure sentinel. - ExternalReference pending_exception(Isolate::k_pending_exception_address, + ExternalReference pending_exception(Isolate::kPendingExceptionAddress, isolate); __ Store(pending_exception, rax); __ movq(rax, Failure::Exception(), RelocInfo::NONE); @@ -3938,7 +3955,8 @@ void StringCharCodeAtGenerator::GenerateFast(MacroAssembler* masm) { // Check for 1-byte or 2-byte string. __ bind(&flat_string); - STATIC_ASSERT(kAsciiStringTag != 0); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ testb(result_, Immediate(kStringEncodingMask)); __ j(not_zero, &ascii_string); @@ -4195,8 +4213,9 @@ void StringAddStub::Generate(MacroAssembler* masm) { Label non_ascii, allocated, ascii_data; __ movl(rcx, r8); __ and_(rcx, r9); - STATIC_ASSERT(kStringEncodingMask == kAsciiStringTag); - __ testl(rcx, Immediate(kAsciiStringTag)); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ testl(rcx, Immediate(kStringEncodingMask)); __ j(zero, &non_ascii); __ bind(&ascii_data); // Allocate an acsii cons string. @@ -4225,7 +4244,7 @@ void StringAddStub::Generate(MacroAssembler* masm) { __ cmpb(r8, Immediate(kAsciiStringTag | kAsciiDataHintTag)); __ j(equal, &ascii_data); // Allocate a two byte cons string. - __ AllocateConsString(rcx, rdi, no_reg, &string_add_runtime); + __ AllocateTwoByteConsString(rcx, rdi, no_reg, &string_add_runtime); __ jmp(&allocated); // Handle creating a flat result. First check that both strings are not @@ -4254,10 +4273,11 @@ void StringAddStub::Generate(MacroAssembler* masm) { // r8: instance type of first string // r9: instance type of second string Label non_ascii_string_add_flat_result; - STATIC_ASSERT(kStringEncodingMask == kAsciiStringTag); - __ testl(r8, Immediate(kAsciiStringTag)); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ testl(r8, Immediate(kStringEncodingMask)); __ j(zero, &non_ascii_string_add_flat_result); - __ testl(r9, Immediate(kAsciiStringTag)); + __ testl(r9, Immediate(kStringEncodingMask)); __ j(zero, &string_add_runtime); __ bind(&make_flat_ascii_string); @@ -4295,7 +4315,9 @@ void StringAddStub::Generate(MacroAssembler* masm) { // r8: instance type of first string // r9: instance type of first string __ bind(&non_ascii_string_add_flat_result); - __ and_(r9, Immediate(kAsciiStringTag)); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ and_(r9, Immediate(kStringEncodingMask)); __ j(not_zero, &string_add_runtime); // Both strings are two byte strings. As they are short they are both // flat. @@ -4639,9 +4661,6 @@ void StringHelper::GenerateHashGetHash(MacroAssembler* masm, void SubStringStub::Generate(MacroAssembler* masm) { Label runtime; - if (FLAG_string_slices) { - __ jmp(&runtime); - } // Stack frame on entry. // rsp[0]: return address // rsp[8]: to @@ -4707,7 +4726,82 @@ void SubStringStub::Generate(MacroAssembler* masm) { __ movzxbl(rbx, FieldOperand(rbx, Map::kInstanceTypeOffset)); __ Set(rcx, 2); - __ bind(&result_longer_than_two); + if (FLAG_string_slices) { + Label copy_routine; + // If coming from the make_two_character_string path, the string + // is too short to be sliced anyways. + STATIC_ASSERT(2 < SlicedString::kMinLength); + __ jmp(©_routine); + __ bind(&result_longer_than_two); + + // rax: string + // rbx: instance type + // rcx: sub string length + // rdx: from index (smi) + Label allocate_slice, sliced_string, seq_string; + __ cmpq(rcx, Immediate(SlicedString::kMinLength)); + // Short slice. Copy instead of slicing. + __ j(less, ©_routine); + STATIC_ASSERT(kSeqStringTag == 0); + __ testb(rbx, Immediate(kStringRepresentationMask)); + __ j(zero, &seq_string, Label::kNear); + STATIC_ASSERT(kIsIndirectStringMask == (kSlicedStringTag & kConsStringTag)); + STATIC_ASSERT(kIsIndirectStringMask != 0); + __ testb(rbx, Immediate(kIsIndirectStringMask)); + // External string. Jump to runtime. + __ j(zero, &runtime); + + __ testb(rbx, Immediate(kSlicedNotConsMask)); + __ j(not_zero, &sliced_string, Label::kNear); + // Cons string. Check whether it is flat, then fetch first part. + __ CompareRoot(FieldOperand(rax, ConsString::kSecondOffset), + Heap::kEmptyStringRootIndex); + __ j(not_equal, &runtime); + __ movq(rdi, FieldOperand(rax, ConsString::kFirstOffset)); + __ jmp(&allocate_slice, Label::kNear); + + __ bind(&sliced_string); + // Sliced string. Fetch parent and correct start index by offset. + __ addq(rdx, FieldOperand(rax, SlicedString::kOffsetOffset)); + __ movq(rdi, FieldOperand(rax, SlicedString::kParentOffset)); + __ jmp(&allocate_slice, Label::kNear); + + __ bind(&seq_string); + // Sequential string. Just move string to the right register. + __ movq(rdi, rax); + + __ bind(&allocate_slice); + // edi: underlying subject string + // ebx: instance type of original subject string + // edx: offset + // ecx: length + // Allocate new sliced string. At this point we do not reload the instance + // type including the string encoding because we simply rely on the info + // provided by the original string. It does not matter if the original + // string's encoding is wrong because we always have to recheck encoding of + // the newly created string's parent anyways due to externalized strings. + Label two_byte_slice, set_slice_header; + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); + __ testb(rbx, Immediate(kStringEncodingMask)); + __ j(zero, &two_byte_slice, Label::kNear); + __ AllocateAsciiSlicedString(rax, rbx, no_reg, &runtime); + __ jmp(&set_slice_header, Label::kNear); + __ bind(&two_byte_slice); + __ AllocateTwoByteSlicedString(rax, rbx, no_reg, &runtime); + __ bind(&set_slice_header); + __ movq(FieldOperand(rax, SlicedString::kOffsetOffset), rdx); + __ Integer32ToSmi(rcx, rcx); + __ movq(FieldOperand(rax, SlicedString::kLengthOffset), rcx); + __ movq(FieldOperand(rax, SlicedString::kParentOffset), rdi); + __ movq(FieldOperand(rax, SlicedString::kHashFieldOffset), + Immediate(String::kEmptyHashField)); + __ jmp(&return_rax); + + __ bind(©_routine); + } else { + __ bind(&result_longer_than_two); + } // rax: string // rbx: instance type diff --git a/src/x64/full-codegen-x64.cc b/src/x64/full-codegen-x64.cc index eca349eb..556523fa 100644 --- a/src/x64/full-codegen-x64.cc +++ b/src/x64/full-codegen-x64.cc @@ -51,8 +51,7 @@ static unsigned GetPropertyId(Property* property) { class JumpPatchSite BASE_EMBEDDED { public: - explicit JumpPatchSite(MacroAssembler* masm) - : masm_(masm) { + explicit JumpPatchSite(MacroAssembler* masm) : masm_(masm) { #ifdef DEBUG info_emitted_ = false; #endif @@ -187,14 +186,14 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { // Copy any necessary parameters into the context. int num_parameters = info->scope()->num_parameters(); for (int i = 0; i < num_parameters; i++) { - Slot* slot = scope()->parameter(i)->AsSlot(); - if (slot != NULL && slot->type() == Slot::CONTEXT) { + Variable* var = scope()->parameter(i); + if (var->IsContextSlot()) { int parameter_offset = StandardFrameConstants::kCallerSPOffset + (num_parameters - 1 - i) * kPointerSize; // Load parameter from stack. __ movq(rax, Operand(rbp, parameter_offset)); // Store it in the context. - int context_offset = Context::SlotOffset(slot->index()); + int context_offset = Context::SlotOffset(var->index()); __ movq(Operand(rsi, context_offset), rax); // Update the write barrier. This clobbers all involved // registers, so we have use a third register to avoid @@ -232,7 +231,7 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { : ArgumentsAccessStub::NEW_NON_STRICT_SLOW); __ CallStub(&stub); - Move(arguments->AsSlot(), rax, rbx, rdx); + SetVar(arguments, rax, rbx, rdx); } if (FLAG_trace) { @@ -244,18 +243,21 @@ void FullCodeGenerator::Generate(CompilationInfo* info) { if (scope()->HasIllegalRedeclaration()) { Comment cmnt(masm_, "[ Declarations"); scope()->VisitIllegalRedeclaration(this); + } else { + PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS); { Comment cmnt(masm_, "[ Declarations"); // For named function expressions, declare the function name as a // constant. if (scope()->is_function_scope() && scope()->function() != NULL) { - EmitDeclaration(scope()->function(), Variable::CONST, NULL); + int ignored = 0; + EmitDeclaration(scope()->function(), Variable::CONST, NULL, &ignored); } VisitDeclarations(scope()->declarations()); } { Comment cmnt(masm_, "[ Stack check"); - PrepareForBailoutForId(AstNode::kFunctionEntryId, NO_REGISTERS); + PrepareForBailoutForId(AstNode::kDeclarationsId, NO_REGISTERS); Label ok; __ CompareRoot(rsp, Heap::kStackLimitRootIndex); __ j(above_equal, &ok, Label::kNear); @@ -355,24 +357,26 @@ void FullCodeGenerator::EmitReturnSequence() { } -void FullCodeGenerator::EffectContext::Plug(Slot* slot) const { +void FullCodeGenerator::EffectContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); } -void FullCodeGenerator::AccumulatorValueContext::Plug(Slot* slot) const { - MemOperand slot_operand = codegen()->EmitSlotSearch(slot, result_register()); - __ movq(result_register(), slot_operand); +void FullCodeGenerator::AccumulatorValueContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + codegen()->GetVar(result_register(), var); } -void FullCodeGenerator::StackValueContext::Plug(Slot* slot) const { - MemOperand slot_operand = codegen()->EmitSlotSearch(slot, result_register()); - __ push(slot_operand); +void FullCodeGenerator::StackValueContext::Plug(Variable* var) const { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + MemOperand operand = codegen()->VarOperand(var, result_register()); + __ push(operand); } -void FullCodeGenerator::TestContext::Plug(Slot* slot) const { - codegen()->Move(result_register(), slot); +void FullCodeGenerator::TestContext::Plug(Variable* var) const { + codegen()->GetVar(result_register(), var); codegen()->PrepareForBailoutBeforeSplit(TOS_REG, false, NULL, NULL); codegen()->DoTest(this); } @@ -591,43 +595,53 @@ void FullCodeGenerator::Split(Condition cc, } -MemOperand FullCodeGenerator::EmitSlotSearch(Slot* slot, Register scratch) { - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: - return Operand(rbp, SlotOffset(slot)); - case Slot::CONTEXT: { - int context_chain_length = - scope()->ContextChainLength(slot->var()->scope()); - __ LoadContext(scratch, context_chain_length); - return ContextOperand(scratch, slot->index()); - } - case Slot::LOOKUP: - UNREACHABLE(); +MemOperand FullCodeGenerator::StackOperand(Variable* var) { + ASSERT(var->IsStackAllocated()); + // Offset is negative because higher indexes are at lower addresses. + int offset = -var->index() * kPointerSize; + // Adjust by a (parameter or local) base offset. + if (var->IsParameter()) { + offset += (info_->scope()->num_parameters() + 1) * kPointerSize; + } else { + offset += JavaScriptFrameConstants::kLocal0Offset; } - UNREACHABLE(); - return Operand(rax, 0); + return Operand(rbp, offset); } -void FullCodeGenerator::Move(Register destination, Slot* source) { - MemOperand location = EmitSlotSearch(source, destination); - __ movq(destination, location); +MemOperand FullCodeGenerator::VarOperand(Variable* var, Register scratch) { + ASSERT(var->IsContextSlot() || var->IsStackAllocated()); + if (var->IsContextSlot()) { + int context_chain_length = scope()->ContextChainLength(var->scope()); + __ LoadContext(scratch, context_chain_length); + return ContextOperand(scratch, var->index()); + } else { + return StackOperand(var); + } } -void FullCodeGenerator::Move(Slot* dst, - Register src, - Register scratch1, - Register scratch2) { - ASSERT(dst->type() != Slot::LOOKUP); // Not yet implemented. - ASSERT(!scratch1.is(src) && !scratch2.is(src)); - MemOperand location = EmitSlotSearch(dst, scratch1); +void FullCodeGenerator::GetVar(Register dest, Variable* var) { + ASSERT(var->IsContextSlot() || var->IsStackAllocated()); + MemOperand location = VarOperand(var, dest); + __ movq(dest, location); +} + + +void FullCodeGenerator::SetVar(Variable* var, + Register src, + Register scratch0, + Register scratch1) { + ASSERT(var->IsContextSlot() || var->IsStackAllocated()); + ASSERT(!scratch0.is(src)); + ASSERT(!scratch0.is(scratch1)); + ASSERT(!scratch1.is(src)); + MemOperand location = VarOperand(var, scratch0); __ movq(location, src); // Emit the write barrier code if the location is in the heap. - if (dst->type() == Slot::CONTEXT) { - int offset = FixedArray::kHeaderSize + dst->index() * kPointerSize; - __ RecordWrite(scratch1, offset, src, scratch2); + if (var->IsContextSlot()) { + int offset = Context::SlotOffset(var->index()); + __ RecordWrite(scratch0, offset, src, scratch1); } } @@ -658,29 +672,33 @@ void FullCodeGenerator::PrepareForBailoutBeforeSplit(State state, } -void FullCodeGenerator::EmitDeclaration(Variable* variable, +void FullCodeGenerator::EmitDeclaration(VariableProxy* proxy, Variable::Mode mode, - FunctionLiteral* function) { - Comment cmnt(masm_, "[ Declaration"); - ASSERT(variable != NULL); // Must have been resolved. - Slot* slot = variable->AsSlot(); - ASSERT(slot != NULL); - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: + FunctionLiteral* function, + int* global_count) { + // If it was not possible to allocate the variable at compile time, we + // need to "declare" it at runtime to make sure it actually exists in the + // local context. + Variable* variable = proxy->var(); + switch (variable->location()) { + case Variable::UNALLOCATED: + ++(*global_count); + break; + + case Variable::PARAMETER: + case Variable::LOCAL: if (function != NULL) { + Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); - __ movq(Operand(rbp, SlotOffset(slot)), result_register()); + __ movq(StackOperand(variable), result_register()); } else if (mode == Variable::CONST || mode == Variable::LET) { + Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); - __ movq(Operand(rbp, SlotOffset(slot)), kScratchRegister); + __ movq(StackOperand(variable), kScratchRegister); } break; - case Slot::CONTEXT: - // We bypass the general EmitSlotSearch because we know more about - // this specific context. - + case Variable::CONTEXT: // The variable in the decl always resides in the current function // context. ASSERT_EQ(0, scope()->ContextChainLength(variable->scope())); @@ -693,22 +711,27 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, __ Check(not_equal, "Declaration in catch context."); } if (function != NULL) { + Comment cmnt(masm_, "[ Declaration"); VisitForAccumulatorValue(function); - __ movq(ContextOperand(rsi, slot->index()), result_register()); - int offset = Context::SlotOffset(slot->index()); + __ movq(ContextOperand(rsi, variable->index()), result_register()); + int offset = Context::SlotOffset(variable->index()); __ movq(rbx, rsi); __ RecordWrite(rbx, offset, result_register(), rcx); + PrepareForBailoutForId(proxy->id(), NO_REGISTERS); } else if (mode == Variable::CONST || mode == Variable::LET) { + Comment cmnt(masm_, "[ Declaration"); __ LoadRoot(kScratchRegister, Heap::kTheHoleValueRootIndex); - __ movq(ContextOperand(rsi, slot->index()), kScratchRegister); + __ movq(ContextOperand(rsi, variable->index()), kScratchRegister); // No write barrier since the hole value is in old space. + PrepareForBailoutForId(proxy->id(), NO_REGISTERS); } break; - case Slot::LOOKUP: { + case Variable::LOOKUP: { + Comment cmnt(masm_, "[ Declaration"); __ push(rsi); __ Push(variable->name()); - // Declaration nodes are always introduced in one of two modes. + // Declaration nodes are always introduced in one of three modes. ASSERT(mode == Variable::VAR || mode == Variable::CONST || mode == Variable::LET); @@ -723,7 +746,7 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, } else if (mode == Variable::CONST || mode == Variable::LET) { __ PushRoot(Heap::kTheHoleValueRootIndex); } else { - __ Push(Smi::FromInt(0)); // no initial value! + __ Push(Smi::FromInt(0)); // Indicates no initial value. } __ CallRuntime(Runtime::kDeclareContextSlot, 4); break; @@ -732,18 +755,15 @@ void FullCodeGenerator::EmitDeclaration(Variable* variable, } -void FullCodeGenerator::VisitDeclaration(Declaration* decl) { - EmitDeclaration(decl->proxy()->var(), decl->mode(), decl->fun()); -} +void FullCodeGenerator::VisitDeclaration(Declaration* decl) { } void FullCodeGenerator::DeclareGlobals(Handle<FixedArray> pairs) { // Call the runtime to declare the globals. __ push(rsi); // The context is the first argument. __ Push(pairs); - __ Push(Smi::FromInt(is_eval() ? 1 : 0)); - __ Push(Smi::FromInt(strict_mode_flag())); - __ CallRuntime(Runtime::kDeclareGlobals, 4); + __ Push(Smi::FromInt(DeclareGlobalsFlags())); + __ CallRuntime(Runtime::kDeclareGlobals, 3); // Return value is ignored. } @@ -1048,10 +1068,9 @@ void FullCodeGenerator::VisitVariableProxy(VariableProxy* expr) { } -void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( - Slot* slot, - TypeofState typeof_state, - Label* slow) { +void FullCodeGenerator::EmitLoadGlobalCheckExtensions(Variable* var, + TypeofState typeof_state, + Label* slow) { Register context = rsi; Register temp = rdx; @@ -1101,7 +1120,7 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( // All extension objects were empty and it is safe to use a global // load IC call. __ movq(rax, GlobalObjectOperand()); - __ Move(rcx, slot->var()->name()); + __ Move(rcx, var->name()); Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); RelocInfo::Mode mode = (typeof_state == INSIDE_TYPEOF) ? RelocInfo::CODE_TARGET @@ -1110,14 +1129,13 @@ void FullCodeGenerator::EmitLoadGlobalSlotCheckExtensions( } -MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( - Slot* slot, - Label* slow) { - ASSERT(slot->type() == Slot::CONTEXT); +MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions(Variable* var, + Label* slow) { + ASSERT(var->IsContextSlot()); Register context = rsi; Register temp = rbx; - for (Scope* s = scope(); s != slot->var()->scope(); s = s->outer_scope()) { + for (Scope* s = scope(); s != var->scope(); s = s->outer_scope()) { if (s->num_heap_slots() > 0) { if (s->calls_eval()) { // Check that extension is NULL. @@ -1137,60 +1155,31 @@ MemOperand FullCodeGenerator::ContextSlotOperandCheckExtensions( // This function is used only for loads, not stores, so it's safe to // return an rsi-based operand (the write barrier cannot be allowed to // destroy the rsi register). - return ContextOperand(context, slot->index()); + return ContextOperand(context, var->index()); } -void FullCodeGenerator::EmitDynamicLoadFromSlotFastCase( - Slot* slot, - TypeofState typeof_state, - Label* slow, - Label* done) { +void FullCodeGenerator::EmitDynamicLookupFastCase(Variable* var, + TypeofState typeof_state, + Label* slow, + Label* done) { // Generate fast-case code for variables that might be shadowed by // eval-introduced variables. Eval is used a lot without // introducing variables. In those cases, we do not want to // perform a runtime call for all variables in the scope // containing the eval. - if (slot->var()->mode() == Variable::DYNAMIC_GLOBAL) { - EmitLoadGlobalSlotCheckExtensions(slot, typeof_state, slow); + if (var->mode() == Variable::DYNAMIC_GLOBAL) { + EmitLoadGlobalCheckExtensions(var, typeof_state, slow); __ jmp(done); - } else if (slot->var()->mode() == Variable::DYNAMIC_LOCAL) { - Slot* potential_slot = slot->var()->local_if_not_shadowed()->AsSlot(); - Expression* rewrite = slot->var()->local_if_not_shadowed()->rewrite(); - if (potential_slot != NULL) { - // Generate fast case for locals that rewrite to slots. - __ movq(rax, - ContextSlotOperandCheckExtensions(potential_slot, slow)); - if (potential_slot->var()->mode() == Variable::CONST) { - __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); - __ j(not_equal, done); - __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); - } - __ jmp(done); - } else if (rewrite != NULL) { - // Generate fast case for calls of an argument function. - Property* property = rewrite->AsProperty(); - if (property != NULL) { - VariableProxy* obj_proxy = property->obj()->AsVariableProxy(); - Literal* key_literal = property->key()->AsLiteral(); - if (obj_proxy != NULL && - key_literal != NULL && - obj_proxy->IsArguments() && - key_literal->handle()->IsSmi()) { - // Load arguments object if there are no eval-introduced - // variables. Then load the argument from the arguments - // object using keyed load. - __ movq(rdx, - ContextSlotOperandCheckExtensions(obj_proxy->var()->AsSlot(), - slow)); - __ Move(rax, key_literal->handle()); - Handle<Code> ic = - isolate()->builtins()->KeyedLoadIC_Initialize(); - __ call(ic, RelocInfo::CODE_TARGET, GetPropertyId(property)); - __ jmp(done); - } - } + } else if (var->mode() == Variable::DYNAMIC_LOCAL) { + Variable* local = var->local_if_not_shadowed(); + __ movq(rax, ContextSlotOperandCheckExtensions(local, slow)); + if (local->mode() == Variable::CONST) { + __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); + __ j(not_equal, done); + __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); } + __ jmp(done); } } @@ -1200,66 +1189,58 @@ void FullCodeGenerator::EmitVariableLoad(VariableProxy* proxy) { SetSourcePosition(proxy->position()); Variable* var = proxy->var(); - // Three cases: non-this global variables, lookup slots, and all other - // types of slots. - Slot* slot = var->AsSlot(); - ASSERT((var->is_global() && !var->is_this()) == (slot == NULL)); - - if (slot == NULL) { - Comment cmnt(masm_, "Global variable"); - // Use inline caching. Variable name is passed in rcx and the global - // object on the stack. - __ Move(rcx, var->name()); - __ movq(rax, GlobalObjectOperand()); - Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); - __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); - context()->Plug(rax); - - } else if (slot != NULL && slot->type() == Slot::LOOKUP) { - Label done, slow; - - // Generate code for loading from variables potentially shadowed - // by eval-introduced variables. - EmitDynamicLoadFromSlotFastCase(slot, NOT_INSIDE_TYPEOF, &slow, &done); - - __ bind(&slow); - Comment cmnt(masm_, "Lookup slot"); - __ push(rsi); // Context. - __ Push(var->name()); - __ CallRuntime(Runtime::kLoadContextSlot, 2); - __ bind(&done); + // Three cases: global variables, lookup variables, and all other types of + // variables. + switch (var->location()) { + case Variable::UNALLOCATED: { + Comment cmnt(masm_, "Global variable"); + // Use inline caching. Variable name is passed in rcx and the global + // object on the stack. + __ Move(rcx, var->name()); + __ movq(rax, GlobalObjectOperand()); + Handle<Code> ic = isolate()->builtins()->LoadIC_Initialize(); + __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); + context()->Plug(rax); + break; + } - context()->Plug(rax); + case Variable::PARAMETER: + case Variable::LOCAL: + case Variable::CONTEXT: { + Comment cmnt(masm_, var->IsContextSlot() ? "Context slot" : "Stack slot"); + if (var->mode() != Variable::LET && var->mode() != Variable::CONST) { + context()->Plug(var); + } else { + // Let and const need a read barrier. + Label done; + GetVar(rax, var); + __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); + __ j(not_equal, &done, Label::kNear); + if (var->mode() == Variable::LET) { + __ Push(var->name()); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + } else { // Variable::CONST + __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); + } + __ bind(&done); + context()->Plug(rax); + } + break; + } - } else { - Comment cmnt(masm_, (slot->type() == Slot::CONTEXT) - ? "Context slot" - : "Stack slot"); - if (var->mode() == Variable::CONST) { - // Constants may be the hole value if they have not been initialized. - // Unhole them. - Label done; - MemOperand slot_operand = EmitSlotSearch(slot, rax); - __ movq(rax, slot_operand); - __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); - __ j(not_equal, &done, Label::kNear); - __ LoadRoot(rax, Heap::kUndefinedValueRootIndex); - __ bind(&done); - context()->Plug(rax); - } else if (var->mode() == Variable::LET) { - // Let bindings may be the hole value if they have not been initialized. - // Throw a type error in this case. - Label done; - MemOperand slot_operand = EmitSlotSearch(slot, rax); - __ movq(rax, slot_operand); - __ CompareRoot(rax, Heap::kTheHoleValueRootIndex); - __ j(not_equal, &done, Label::kNear); + case Variable::LOOKUP: { + Label done, slow; + // Generate code for loading from variables potentially shadowed + // by eval-introduced variables. + EmitDynamicLookupFastCase(var, NOT_INSIDE_TYPEOF, &slow, &done); + __ bind(&slow); + Comment cmnt(masm_, "Lookup slot"); + __ push(rsi); // Context. __ Push(var->name()); - __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ CallRuntime(Runtime::kLoadContextSlot, 2); __ bind(&done); context()->Plug(rax); - } else { - context()->Plug(slot); + break; } } } @@ -1279,7 +1260,7 @@ void FullCodeGenerator::VisitRegExpLiteral(RegExpLiteral* expr) { FixedArray::kHeaderSize + expr->literal_index() * kPointerSize; __ movq(rbx, FieldOperand(rcx, literal_offset)); __ CompareRoot(rbx, Heap::kUndefinedValueRootIndex); - __ j(not_equal, &materialized); + __ j(not_equal, &materialized, Label::kNear); // Create regexp literal using runtime function // Result will be in rax. @@ -1744,133 +1725,88 @@ void FullCodeGenerator::EmitAssignment(Expression* expr, int bailout_ast_id) { void FullCodeGenerator::EmitVariableAssignment(Variable* var, Token::Value op) { - ASSERT(var != NULL); - ASSERT(var->is_global() || var->AsSlot() != NULL); - - if (var->is_global()) { - ASSERT(!var->is_this()); - // Assignment to a global variable. Use inline caching for the - // assignment. Right-hand-side value is passed in rax, variable name in - // rcx, and the global object on the stack. + if (var->IsUnallocated()) { + // Global var, const, or let. __ Move(rcx, var->name()); __ movq(rdx, GlobalObjectOperand()); Handle<Code> ic = is_strict_mode() ? isolate()->builtins()->StoreIC_Initialize_Strict() : isolate()->builtins()->StoreIC_Initialize(); __ call(ic, RelocInfo::CODE_TARGET_CONTEXT); - } else if (op == Token::INIT_CONST) { - // Like var declarations, const declarations are hoisted to function - // scope. However, unlike var initializers, const initializers are able - // to drill a hole to that function context, even from inside a 'with' - // context. We thus bypass the normal static scope lookup. - Slot* slot = var->AsSlot(); - Label skip; - switch (slot->type()) { - case Slot::PARAMETER: - // No const parameters. - UNREACHABLE(); - break; - case Slot::LOCAL: - __ movq(rdx, Operand(rbp, SlotOffset(slot))); - __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); - __ j(not_equal, &skip); - __ movq(Operand(rbp, SlotOffset(slot)), rax); - break; - case Slot::CONTEXT: - case Slot::LOOKUP: - __ push(rax); - __ push(rsi); - __ Push(var->name()); - __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); - break; + // Const initializers need a write barrier. + ASSERT(!var->IsParameter()); // No const parameters. + if (var->IsStackLocal()) { + Label skip; + __ movq(rdx, StackOperand(var)); + __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); + __ j(not_equal, &skip); + __ movq(StackOperand(var), rax); + __ bind(&skip); + } else { + ASSERT(var->IsContextSlot() || var->IsLookupSlot()); + // Like var declarations, const declarations are hoisted to function + // scope. However, unlike var initializers, const initializers are + // able to drill a hole to that function context, even from inside a + // 'with' context. We thus bypass the normal static scope lookup for + // var->IsContextSlot(). + __ push(rax); + __ push(rsi); + __ Push(var->name()); + __ CallRuntime(Runtime::kInitializeConstContextSlot, 3); } - __ bind(&skip); } else if (var->mode() == Variable::LET && op != Token::INIT_LET) { - // Perform the assignment for non-const variables. Const assignments - // are simply skipped. - Slot* slot = var->AsSlot(); - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: { - Label assign; - // Check for an initialized let binding. - __ movq(rdx, Operand(rbp, SlotOffset(slot))); - __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); - __ j(not_equal, &assign); - __ Push(var->name()); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - // Perform the assignment. - __ bind(&assign); - __ movq(Operand(rbp, SlotOffset(slot)), rax); - break; - } - - case Slot::CONTEXT: { - // Let variables may be the hole value if they have not been - // initialized. Throw a type error in this case. - Label assign; - MemOperand target = EmitSlotSearch(slot, rcx); - // Check for an initialized let binding. - __ movq(rdx, target); - __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); - __ j(not_equal, &assign, Label::kNear); - __ Push(var->name()); - __ CallRuntime(Runtime::kThrowReferenceError, 1); - // Perform the assignment. - __ bind(&assign); - __ movq(target, rax); - // The value of the assignment is in eax. RecordWrite clobbers its - // register arguments. + // Non-initializing assignment to let variable needs a write barrier. + if (var->IsLookupSlot()) { + __ push(rax); // Value. + __ push(rsi); // Context. + __ Push(var->name()); + __ Push(Smi::FromInt(strict_mode_flag())); + __ CallRuntime(Runtime::kStoreContextSlot, 4); + } else { + ASSERT(var->IsStackAllocated() || var->IsContextSlot()); + Label assign; + MemOperand location = VarOperand(var, rcx); + __ movq(rdx, location); + __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); + __ j(not_equal, &assign, Label::kNear); + __ Push(var->name()); + __ CallRuntime(Runtime::kThrowReferenceError, 1); + __ bind(&assign); + __ movq(location, rax); + if (var->IsContextSlot()) { __ movq(rdx, rax); - int offset = Context::SlotOffset(slot->index()); - __ RecordWrite(rcx, offset, rdx, rbx); - break; + __ RecordWrite(rcx, Context::SlotOffset(var->index()), rdx, rbx); } - - case Slot::LOOKUP: - // Call the runtime for the assignment. - __ push(rax); // Value. - __ push(rsi); // Context. - __ Push(var->name()); - __ Push(Smi::FromInt(strict_mode_flag())); - __ CallRuntime(Runtime::kStoreContextSlot, 4); - break; } - } else if (var->mode() != Variable::CONST) { - // Perform the assignment for non-const variables. Const assignments - // are simply skipped. - Slot* slot = var->AsSlot(); - switch (slot->type()) { - case Slot::PARAMETER: - case Slot::LOCAL: - // Perform the assignment. - __ movq(Operand(rbp, SlotOffset(slot)), rax); - break; - case Slot::CONTEXT: { - MemOperand target = EmitSlotSearch(slot, rcx); - // Perform the assignment and issue the write barrier. - __ movq(target, rax); - // The value of the assignment is in rax. RecordWrite clobbers its - // register arguments. + } else if (var->mode() != Variable::CONST) { + // Assignment to var or initializing assignment to let. + if (var->IsStackAllocated() || var->IsContextSlot()) { + MemOperand location = VarOperand(var, rcx); + if (FLAG_debug_code && op == Token::INIT_LET) { + // Check for an uninitialized let binding. + __ movq(rdx, location); + __ CompareRoot(rdx, Heap::kTheHoleValueRootIndex); + __ Check(equal, "Let binding re-initialization."); + } + // Perform the assignment. + __ movq(location, rax); + if (var->IsContextSlot()) { __ movq(rdx, rax); - int offset = Context::SlotOffset(slot->index()); - __ RecordWrite(rcx, offset, rdx, rbx); - break; + __ RecordWrite(rcx, Context::SlotOffset(var->index()), rdx, rbx); } - - case Slot::LOOKUP: - // Call the runtime for the assignment. - __ push(rax); // Value. - __ push(rsi); // Context. - __ Push(var->name()); - __ Push(Smi::FromInt(strict_mode_flag())); - __ CallRuntime(Runtime::kStoreContextSlot, 4); - break; + } else { + ASSERT(var->IsLookupSlot()); + __ push(rax); // Value. + __ push(rsi); // Context. + __ Push(var->name()); + __ Push(Smi::FromInt(strict_mode_flag())); + __ CallRuntime(Runtime::kStoreContextSlot, 4); } } + // Non-initializing assignments to consts are ignored. } @@ -1990,9 +1926,8 @@ void FullCodeGenerator::EmitCallWithIC(Call* expr, // 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. @@ -2023,9 +1958,8 @@ void FullCodeGenerator::EmitKeyedCallWithIC(Call* expr, // 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); @@ -2046,8 +1980,7 @@ void FullCodeGenerator::EmitCallWithStub(Call* expr, CallFunctionFlags flags) { } // 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. @@ -2069,8 +2002,13 @@ void FullCodeGenerator::EmitResolvePossiblyDirectEval(ResolveEvalFlag flag, // Push the receiver of the enclosing function and do runtime call. __ push(Operand(rbp, (2 + info_->scope()->num_parameters()) * kPointerSize)); - // Push the strict mode flag. - __ Push(Smi::FromInt(strict_mode_flag())); + // Push the strict mode flag. In harmony mode every eval call + // is a strict mode eval call. + StrictModeFlag strict_mode = strict_mode_flag(); + if (FLAG_harmony_block_scoping) { + strict_mode = kStrictMode; + } + __ Push(Smi::FromInt(strict_mode)); __ CallRuntime(flag == SKIP_CONTEXT_LOOKUP ? Runtime::kResolvePossiblyDirectEvalNoLookup @@ -2086,18 +2024,18 @@ void FullCodeGenerator::VisitCall(Call* expr) { #endif Comment cmnt(masm_, "[ Call"); - Expression* fun = expr->expression(); - Variable* var = fun->AsVariableProxy()->AsVariable(); + Expression* callee = expr->expression(); + VariableProxy* proxy = callee->AsVariableProxy(); + Property* property = callee->AsProperty(); - if (var != NULL && var->is_possibly_eval()) { + if (proxy != NULL && proxy->var()->is_possibly_eval()) { // In a call to eval, we first call %ResolvePossiblyDirectEval to - // resolve the function we need to call and the receiver of the - // call. Then we call the resolved function using the given - // arguments. + // resolve the function we need to call and the receiver of the call. + // Then we call the resolved function using the given arguments. ZoneList<Expression*>* args = expr->arguments(); int arg_count = args->length(); { PreservePositionScope pos_scope(masm()->positions_recorder()); - VisitForStackValue(fun); + VisitForStackValue(callee); __ PushRoot(Heap::kUndefinedValueRootIndex); // Reserved receiver slot. // Push the arguments. @@ -2106,15 +2044,14 @@ void FullCodeGenerator::VisitCall(Call* expr) { } // If we know that eval can only be shadowed by eval-introduced - // variables we attempt to load the global eval function directly - // in generated code. If we succeed, there is no need to perform a + // variables we attempt to load the global eval function directly in + // generated code. If we succeed, there is no need to perform a // context lookup in the runtime system. Label done; - if (var->AsSlot() != NULL && var->mode() == Variable::DYNAMIC_GLOBAL) { + Variable* var = proxy->var(); + if (!var->IsUnallocated() && var->mode() == Variable::DYNAMIC_GLOBAL) { Label slow; - EmitLoadGlobalSlotCheckExtensions(var->AsSlot(), - NOT_INSIDE_TYPEOF, - &slow); + EmitLoadGlobalCheckExtensions(var, NOT_INSIDE_TYPEOF, &slow); // Push the function and resolve eval. __ push(rax); EmitResolvePossiblyDirectEval(SKIP_CONTEXT_LOOKUP, arg_count); @@ -2122,13 +2059,11 @@ void FullCodeGenerator::VisitCall(Call* expr) { __ bind(&slow); } - // Push copy of the function (found below the arguments) and - // resolve eval. + // Push a copy of the function (found below the arguments) and resolve + // eval. __ push(Operand(rsp, (arg_count + 1) * kPointerSize)); EmitResolvePossiblyDirectEval(PERFORM_CONTEXT_LOOKUP, arg_count); - if (done.is_linked()) { - __ bind(&done); - } + __ bind(&done); // The runtime call returns a pair of values in rax (function) and // rdx (receiver). Touch up the stack with the right values. @@ -2137,80 +2072,68 @@ void FullCodeGenerator::VisitCall(Call* expr) { } // 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. __ movq(rsi, Operand(rbp, StandardFrameConstants::kContextOffset)); context()->DropAndPlug(1, rax); - } else if (var != NULL && !var->is_this() && var->is_global()) { - // Call to a global variable. - // Push global object as receiver for the call IC lookup. + } else if (proxy != NULL && proxy->var()->IsUnallocated()) { + // Call to a global variable. Push global object as receiver for the + // call IC lookup. __ push(GlobalObjectOperand()); - EmitCallWithIC(expr, var->name(), RelocInfo::CODE_TARGET_CONTEXT); - } else if (var != NULL && var->AsSlot() != NULL && - var->AsSlot()->type() == Slot::LOOKUP) { + EmitCallWithIC(expr, proxy->name(), RelocInfo::CODE_TARGET_CONTEXT); + } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { // Call to a lookup slot (dynamically introduced variable). Label slow, done; { PreservePositionScope scope(masm()->positions_recorder()); - // Generate code for loading from variables potentially shadowed - // by eval-introduced variables. - EmitDynamicLoadFromSlotFastCase(var->AsSlot(), - NOT_INSIDE_TYPEOF, - &slow, - &done); - - __ bind(&slow); + // Generate code for loading from variables potentially shadowed by + // eval-introduced variables. + EmitDynamicLookupFastCase(proxy->var(), NOT_INSIDE_TYPEOF, &slow, &done); } - // Call the runtime to find the function to call (returned in rax) - // and the object holding it (returned in rdx). + __ bind(&slow); + // Call the runtime to find the function to call (returned in rax) and + // the object holding it (returned in rdx). __ push(context_register()); - __ Push(var->name()); + __ Push(proxy->name()); __ CallRuntime(Runtime::kLoadContextSlot, 2); __ push(rax); // Function. __ push(rdx); // Receiver. - // If fast case code has been generated, emit code to push the - // function and receiver and have the slow path jump around this - // code. + // If fast case code has been generated, emit code to push the function + // and receiver and have the slow path jump around this code. if (done.is_linked()) { Label call; __ jmp(&call, Label::kNear); __ bind(&done); // Push function. __ push(rax); - // The receiver is implicitly the global receiver. Indicate this - // by passing the hole to the call function stub. + // The receiver is implicitly the global receiver. Indicate this by + // passing the hole to the call function stub. __ PushRoot(Heap::kTheHoleValueRootIndex); __ bind(&call); } - // The receiver is either the global receiver or an object found - // by LoadContextSlot. That object could be the hole if the - // receiver is implicitly the global object. + // The receiver is either the global receiver or an object found by + // LoadContextSlot. That object could be the hole if the receiver is + // implicitly the global object. EmitCallWithStub(expr, RECEIVER_MIGHT_BE_IMPLICIT); - } else if (fun->AsProperty() != NULL) { - // Call to an object property. - Property* prop = fun->AsProperty(); - Literal* key = prop->key()->AsLiteral(); - if (key != NULL && key->handle()->IsSymbol()) { - // Call to a named property, use call IC. - { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(prop->obj()); - } - EmitCallWithIC(expr, key->handle(), RelocInfo::CODE_TARGET); + } else if (property != NULL) { + { PreservePositionScope scope(masm()->positions_recorder()); + VisitForStackValue(property->obj()); + } + if (property->key()->IsPropertyName()) { + EmitCallWithIC(expr, + property->key()->AsLiteral()->handle(), + RelocInfo::CODE_TARGET); } else { - // Call to a keyed property. - { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(prop->obj()); - } - EmitKeyedCallWithIC(expr, prop->key()); + EmitKeyedCallWithIC(expr, property->key()); } } else { + // Call to an arbitrary expression not handled specially above. { PreservePositionScope scope(masm()->positions_recorder()); - VisitForStackValue(fun); + VisitForStackValue(callee); } // Load global receiver object. __ movq(rbx, GlobalObjectOperand()); @@ -3550,10 +3473,9 @@ void FullCodeGenerator::VisitCallRuntime(CallRuntime* expr) { 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)); @@ -3568,30 +3490,31 @@ void FullCodeGenerator::VisitUnaryOperation(UnaryOperation* expr) { switch (expr->op()) { case Token::DELETE: { Comment cmnt(masm_, "[ UnaryOperation (DELETE)"); - Property* prop = expr->expression()->AsProperty(); - Variable* var = expr->expression()->AsVariableProxy()->AsVariable(); + Property* property = expr->expression()->AsProperty(); + VariableProxy* proxy = expr->expression()->AsVariableProxy(); - if (prop != NULL) { - VisitForStackValue(prop->obj()); - VisitForStackValue(prop->key()); + if (property != NULL) { + VisitForStackValue(property->obj()); + VisitForStackValue(property->key()); __ Push(Smi::FromInt(strict_mode_flag())); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); context()->Plug(rax); - } else if (var != NULL) { + } else if (proxy != NULL) { + Variable* var = proxy->var(); // Delete of an unqualified identifier is disallowed in strict mode - // but "delete this" is. + // but "delete this" is allowed. ASSERT(strict_mode_flag() == kNonStrictMode || var->is_this()); - if (var->is_global()) { + if (var->IsUnallocated()) { __ push(GlobalObjectOperand()); __ Push(var->name()); __ Push(Smi::FromInt(kNonStrictMode)); __ InvokeBuiltin(Builtins::DELETE, CALL_FUNCTION); context()->Plug(rax); - } else if (var->AsSlot() != NULL && - var->AsSlot()->type() != Slot::LOOKUP) { - // Result of deleting non-global, non-dynamic variables is false. - // The subexpression does not have side effects. - context()->Plug(false); + } else if (var->IsStackAllocated() || var->IsContextSlot()) { + // Result of deleting non-global variables is false. 'this' is + // not really a variable, though we implement it as one. The + // subexpression does not have side effects. + context()->Plug(var->is_this()); } else { // Non-global variable. Call the runtime to try to delete from the // context where the variable was introduced. @@ -3877,7 +3800,7 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { ASSERT(!context()->IsEffect()); ASSERT(!context()->IsTest()); - if (proxy != NULL && !proxy->var()->is_this() && proxy->var()->is_global()) { + if (proxy != NULL && proxy->var()->IsUnallocated()) { Comment cmnt(masm_, "Global variable"); __ Move(rcx, proxy->name()); __ movq(rax, GlobalObjectOperand()); @@ -3887,15 +3810,12 @@ void FullCodeGenerator::VisitForTypeofValue(Expression* expr) { __ call(ic); PrepareForBailout(expr, TOS_REG); context()->Plug(rax); - } else if (proxy != NULL && - proxy->var()->AsSlot() != NULL && - proxy->var()->AsSlot()->type() == Slot::LOOKUP) { + } else if (proxy != NULL && proxy->var()->IsLookupSlot()) { Label done, slow; // Generate code for loading from variables potentially shadowed // by eval-introduced variables. - Slot* slot = proxy->var()->AsSlot(); - EmitDynamicLoadFromSlotFastCase(slot, INSIDE_TYPEOF, &slow, &done); + EmitDynamicLookupFastCase(proxy->var(), INSIDE_TYPEOF, &slow, &done); __ bind(&slow); __ push(rsi); diff --git a/src/x64/ic-x64.cc b/src/x64/ic-x64.cc index 990c171b..9d55594d 100644 --- a/src/x64/ic-x64.cc +++ b/src/x64/ic-x64.cc @@ -145,7 +145,7 @@ static void GenerateDictionaryLoad(MacroAssembler* masm, const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; __ Test(Operand(elements, r1, times_pointer_size, kDetailsOffset - kHeapObjectTag), - Smi::FromInt(PropertyDetails::TypeField::mask())); + Smi::FromInt(PropertyDetails::TypeField::kMask)); __ j(not_zero, miss_label); // Get the value at the masked, scaled index. @@ -201,9 +201,9 @@ static void GenerateDictionaryStore(MacroAssembler* masm, StringDictionary::kHeaderSize + StringDictionary::kElementsStartIndex * kPointerSize; const int kDetailsOffset = kElementsStartOffset + 2 * kPointerSize; - const int kTypeAndReadOnlyMask - = (PropertyDetails::TypeField::mask() | - PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize; + const int kTypeAndReadOnlyMask = + (PropertyDetails::TypeField::kMask | + PropertyDetails::AttributesField::encode(READ_ONLY)) << kSmiTagSize; __ Test(Operand(elements, scratch1, times_pointer_size, @@ -720,7 +720,6 @@ static void GenerateMonomorphicCacheProbe(MacroAssembler* masm, // Probe the stub cache. Code::Flags flags = Code::ComputeFlags(kind, - NOT_IN_LOOP, MONOMORPHIC, extra_ic_state, NORMAL, @@ -1267,9 +1266,7 @@ void LoadIC::GenerateMegamorphic(MacroAssembler* masm) { // ----------------------------------- // 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 @@ void StoreIC::GenerateMegamorphic(MacroAssembler* masm, // ----------------------------------- // 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 c182413c..9064a266 100644 --- a/src/x64/lithium-codegen-x64.cc +++ b/src/x64/lithium-codegen-x64.cc @@ -100,7 +100,8 @@ void LCodeGen::FinishCode(Handle<Code> code) { void LCodeGen::Abort(const char* format, ...) { if (FLAG_trace_bailout) { - SmartPointer<char> name(info()->shared_info()->DebugName()->ToCString()); + SmartArrayPointer<char> name( + info()->shared_info()->DebugName()->ToCString()); PrintF("Aborting LCodeGen in @\"%s\": ", *name); va_list arguments; va_start(arguments, format); @@ -207,14 +208,14 @@ bool LCodeGen::GeneratePrologue() { // Copy any necessary parameters into the context. int num_parameters = scope()->num_parameters(); for (int i = 0; i < num_parameters; i++) { - Slot* slot = scope()->parameter(i)->AsSlot(); - if (slot != NULL && slot->type() == Slot::CONTEXT) { + Variable* var = scope()->parameter(i); + if (var->IsContextSlot()) { int parameter_offset = StandardFrameConstants::kCallerSPOffset + (num_parameters - 1 - i) * kPointerSize; // Load parameter from stack. __ movq(rax, Operand(rbp, parameter_offset)); // Store it in the context. - int context_offset = Context::SlotOffset(slot->index()); + int context_offset = Context::SlotOffset(var->index()); __ movq(Operand(rsi, context_offset), rax); // Update the write barrier. This clobbers all involved // registers, so we have use a third register to avoid @@ -2219,11 +2220,11 @@ void LCodeGen::DoLoadElements(LLoadElements* instr) { __ movzxbq(temp, FieldOperand(temp, Map::kBitField2Offset)); __ and_(temp, Immediate(Map::kElementsKindMask)); __ shr(temp, Immediate(Map::kElementsKindShift)); - __ cmpl(temp, Immediate(JSObject::FAST_ELEMENTS)); + __ cmpl(temp, Immediate(FAST_ELEMENTS)); __ j(equal, &ok, Label::kNear); - __ cmpl(temp, Immediate(JSObject::FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND)); + __ cmpl(temp, Immediate(FIRST_EXTERNAL_ARRAY_ELEMENTS_KIND)); __ j(less, &fail, Label::kNear); - __ cmpl(temp, Immediate(JSObject::LAST_EXTERNAL_ARRAY_ELEMENTS_KIND)); + __ cmpl(temp, Immediate(LAST_EXTERNAL_ARRAY_ELEMENTS_KIND)); __ j(less_equal, &ok, Label::kNear); __ bind(&fail); __ Abort("Check for fast or external elements failed"); @@ -2267,7 +2268,7 @@ void LCodeGen::DoLoadKeyedFastElement(LLoadKeyedFastElement* instr) { // Load the result. __ movq(result, BuildFastArrayOperand(instr->elements(), instr->key(), - JSObject::FAST_ELEMENTS, + FAST_ELEMENTS, FixedArray::kHeaderSize - kHeapObjectTag)); // Check for the hole value. @@ -2288,14 +2289,14 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( Operand hole_check_operand = BuildFastArrayOperand( instr->elements(), instr->key(), - JSObject::FAST_DOUBLE_ELEMENTS, + FAST_DOUBLE_ELEMENTS, offset); __ cmpl(hole_check_operand, Immediate(kHoleNanUpper32)); DeoptimizeIf(equal, instr->environment()); } Operand double_load_operand = BuildFastArrayOperand( - instr->elements(), instr->key(), JSObject::FAST_DOUBLE_ELEMENTS, + instr->elements(), instr->key(), FAST_DOUBLE_ELEMENTS, FixedDoubleArray::kHeaderSize - kHeapObjectTag); __ movsd(result, double_load_operand); } @@ -2304,7 +2305,7 @@ void LCodeGen::DoLoadKeyedFastDoubleElement( Operand LCodeGen::BuildFastArrayOperand( LOperand* elements_pointer, LOperand* key, - JSObject::ElementsKind elements_kind, + ElementsKind elements_kind, uint32_t offset) { Register elements_pointer_reg = ToRegister(elements_pointer); int shift_size = ElementsKindToShiftSize(elements_kind); @@ -2325,35 +2326,35 @@ Operand LCodeGen::BuildFastArrayOperand( void LCodeGen::DoLoadKeyedSpecializedArrayElement( LLoadKeyedSpecializedArrayElement* instr) { - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); Operand operand(BuildFastArrayOperand(instr->external_pointer(), instr->key(), elements_kind, 0)); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { XMMRegister result(ToDoubleRegister(instr->result())); __ movss(result, operand); __ cvtss2sd(result, result); - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { __ movsd(ToDoubleRegister(instr->result()), operand); } else { Register result(ToRegister(instr->result())); switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: __ movsxbq(result, operand); break; - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: __ movzxbq(result, operand); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: __ movsxwq(result, operand); break; - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ movzxwq(result, operand); break; - case JSObject::EXTERNAL_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: __ movsxlq(result, operand); break; - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ movl(result, operand); __ testl(result, result); // TODO(danno): we could be more clever here, perhaps having a special @@ -2361,12 +2362,12 @@ void LCodeGen::DoLoadKeyedSpecializedArrayElement( // happens, and generate code that returns a double rather than int. DeoptimizeIf(negative, instr->environment()); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -2952,8 +2953,8 @@ void LCodeGen::DoCallKeyed(LCallKeyed* instr) { 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)); } @@ -2965,7 +2966,7 @@ void LCodeGen::DoCallNamed(LCallNamed* instr) { 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)); @@ -2976,7 +2977,7 @@ void LCodeGen::DoCallFunction(LCallFunction* instr) { 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); @@ -2988,7 +2989,7 @@ void LCodeGen::DoCallGlobal(LCallGlobal* instr) { 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)); @@ -3061,37 +3062,37 @@ void LCodeGen::DoStoreNamedGeneric(LStoreNamedGeneric* instr) { void LCodeGen::DoStoreKeyedSpecializedArrayElement( LStoreKeyedSpecializedArrayElement* instr) { - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); Operand operand(BuildFastArrayOperand(instr->external_pointer(), instr->key(), elements_kind, 0)); - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { XMMRegister value(ToDoubleRegister(instr->value())); __ cvtsd2ss(value, value); __ movss(operand, value); - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { __ movsd(operand, ToDoubleRegister(instr->value())); } else { Register value(ToRegister(instr->value())); switch (elements_kind) { - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ movb(operand, value); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ movw(operand, value); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ movl(operand, value); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } @@ -3164,7 +3165,7 @@ void LCodeGen::DoStoreKeyedFastDoubleElement( __ bind(&have_value); Operand double_store_operand = BuildFastArrayOperand( - instr->elements(), instr->key(), JSObject::FAST_DOUBLE_ELEMENTS, + instr->elements(), instr->key(), FAST_DOUBLE_ELEMENTS, FixedDoubleArray::kHeaderSize - kHeapObjectTag); __ movsd(double_store_operand, value); } @@ -3251,7 +3252,8 @@ void LCodeGen::DoStringCharCodeAt(LStringCharCodeAt* instr) { // Dispatch on the encoding: ASCII or two-byte. Label ascii_string; - STATIC_ASSERT(kAsciiStringTag != 0); + STATIC_ASSERT((kStringEncodingMask & kAsciiStringTag) != 0); + STATIC_ASSERT((kStringEncodingMask & kTwoByteStringTag) == 0); __ testb(result, Immediate(kStringEncodingMask)); __ j(not_zero, &ascii_string, Label::kNear); diff --git a/src/x64/lithium-codegen-x64.h b/src/x64/lithium-codegen-x64.h index 0622e9d2..8cb4cece 100644 --- a/src/x64/lithium-codegen-x64.h +++ b/src/x64/lithium-codegen-x64.h @@ -218,7 +218,7 @@ class LCodeGen BASE_EMBEDDED { Operand BuildFastArrayOperand( LOperand* elements_pointer, LOperand* key, - JSObject::ElementsKind elements_kind, + ElementsKind elements_kind, uint32_t offset); // Specific math operations - used from DoUnaryMathOperation. diff --git a/src/x64/lithium-x64.cc b/src/x64/lithium-x64.cc index 9dc925d5..5fc56462 100644 --- a/src/x64/lithium-x64.cc +++ b/src/x64/lithium-x64.cc @@ -313,13 +313,13 @@ void LCallKeyed::PrintDataTo(StringStream* stream) { void LCallNamed::PrintDataTo(StringStream* stream) { - SmartPointer<char> name_string = name()->ToCString(); + SmartArrayPointer<char> name_string = name()->ToCString(); stream->Add("%s #%d / ", *name_string, arity()); } void LCallGlobal::PrintDataTo(StringStream* stream) { - SmartPointer<char> name_string = name()->ToCString(); + SmartArrayPointer<char> name_string = name()->ToCString(); stream->Add("%s #%d / ", *name_string, arity()); } @@ -539,7 +539,8 @@ LChunk* LChunkBuilder::Build() { void LChunkBuilder::Abort(const char* format, ...) { if (FLAG_trace_bailout) { - SmartPointer<char> name(info()->shared_info()->DebugName()->ToCString()); + SmartArrayPointer<char> name( + info()->shared_info()->DebugName()->ToCString()); PrintF("Aborting LChunk building in @\"%s\": ", *name); va_list arguments; va_start(arguments, format); @@ -705,9 +706,7 @@ LInstruction* LChunkBuilder::DefineFixedDouble( LInstruction* LChunkBuilder::AssignEnvironment(LInstruction* instr) { HEnvironment* hydrogen_env = current_block_->last_environment(); - int argument_index_accumulator = 0; - instr->set_environment(CreateEnvironment(hydrogen_env, - &argument_index_accumulator)); + instr->set_environment(CreateEnvironment(hydrogen_env)); return instr; } @@ -990,13 +989,10 @@ void LChunkBuilder::VisitInstruction(HInstruction* current) { } -LEnvironment* LChunkBuilder::CreateEnvironment( - HEnvironment* hydrogen_env, - int* argument_index_accumulator) { +LEnvironment* LChunkBuilder::CreateEnvironment(HEnvironment* hydrogen_env) { if (hydrogen_env == NULL) return NULL; - LEnvironment* outer = - CreateEnvironment(hydrogen_env->outer(), argument_index_accumulator); + LEnvironment* outer = CreateEnvironment(hydrogen_env->outer()); int ast_id = hydrogen_env->ast_id(); ASSERT(ast_id != AstNode::kNoNumber); int value_count = hydrogen_env->length(); @@ -1006,6 +1002,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment( argument_count_, value_count, outer); + int argument_index = 0; for (int i = 0; i < value_count; ++i) { if (hydrogen_env->is_special_index(i)) continue; @@ -1014,7 +1011,7 @@ LEnvironment* LChunkBuilder::CreateEnvironment( if (value->IsArgumentsObject()) { op = NULL; } else if (value->IsPushArgument()) { - op = new LArgument((*argument_index_accumulator)++); + op = new LArgument(argument_index++); } else { op = UseAny(value); } @@ -1844,15 +1841,15 @@ LInstruction* LChunkBuilder::DoLoadKeyedFastDoubleElement( LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( HLoadKeyedSpecializedArrayElement* instr) { - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); Representation representation(instr->representation()); ASSERT( (representation.IsInteger32() && - (elements_kind != JSObject::EXTERNAL_FLOAT_ELEMENTS) && - (elements_kind != JSObject::EXTERNAL_DOUBLE_ELEMENTS)) || + (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && + (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || (representation.IsDouble() && - ((elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) || - (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS)))); + ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || + (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->key()->representation().IsInteger32()); LOperand* external_pointer = UseRegister(instr->external_pointer()); LOperand* key = UseRegisterOrConstant(instr->key()); @@ -1861,7 +1858,7 @@ LInstruction* LChunkBuilder::DoLoadKeyedSpecializedArrayElement( LInstruction* load_instr = DefineAsRegister(result); // An unsigned int array load might overflow and cause a deopt, make sure it // has an environment. - return (elements_kind == JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS) ? + return (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) ? AssignEnvironment(load_instr) : load_instr; } @@ -1911,21 +1908,21 @@ LInstruction* LChunkBuilder::DoStoreKeyedFastDoubleElement( LInstruction* LChunkBuilder::DoStoreKeyedSpecializedArrayElement( HStoreKeyedSpecializedArrayElement* instr) { Representation representation(instr->value()->representation()); - JSObject::ElementsKind elements_kind = instr->elements_kind(); + ElementsKind elements_kind = instr->elements_kind(); ASSERT( (representation.IsInteger32() && - (elements_kind != JSObject::EXTERNAL_FLOAT_ELEMENTS) && - (elements_kind != JSObject::EXTERNAL_DOUBLE_ELEMENTS)) || + (elements_kind != EXTERNAL_FLOAT_ELEMENTS) && + (elements_kind != EXTERNAL_DOUBLE_ELEMENTS)) || (representation.IsDouble() && - ((elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) || - (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS)))); + ((elements_kind == EXTERNAL_FLOAT_ELEMENTS) || + (elements_kind == EXTERNAL_DOUBLE_ELEMENTS)))); ASSERT(instr->external_pointer()->representation().IsExternal()); ASSERT(instr->key()->representation().IsInteger32()); LOperand* external_pointer = UseRegister(instr->external_pointer()); bool val_is_temp_register = - elements_kind == JSObject::EXTERNAL_PIXEL_ELEMENTS || - elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS; + elements_kind == EXTERNAL_PIXEL_ELEMENTS || + elements_kind == EXTERNAL_FLOAT_ELEMENTS; LOperand* val = val_is_temp_register ? UseTempRegister(instr->value()) : UseRegister(instr->value()); diff --git a/src/x64/lithium-x64.h b/src/x64/lithium-x64.h index 05b66376..d169bf6d 100644 --- a/src/x64/lithium-x64.h +++ b/src/x64/lithium-x64.h @@ -1155,7 +1155,7 @@ class LLoadKeyedSpecializedArrayElement: public LTemplateInstruction<1, 2, 0> { LOperand* external_pointer() { return inputs_[0]; } LOperand* key() { return inputs_[1]; } - JSObject::ElementsKind elements_kind() const { + ElementsKind elements_kind() const { return hydrogen()->elements_kind(); } }; @@ -1631,7 +1631,7 @@ class LStoreKeyedSpecializedArrayElement: public LTemplateInstruction<0, 3, 0> { LOperand* external_pointer() { return inputs_[0]; } LOperand* key() { return inputs_[1]; } LOperand* value() { return inputs_[2]; } - JSObject::ElementsKind elements_kind() const { + ElementsKind elements_kind() const { return hydrogen()->elements_kind(); } }; @@ -2146,8 +2146,7 @@ class LChunkBuilder BASE_EMBEDDED { LInstruction* instr, int ast_id); void ClearInstructionPendingDeoptimizationEnvironment(); - LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env, - int* argument_index_accumulator); + LEnvironment* CreateEnvironment(HEnvironment* hydrogen_env); void VisitInstruction(HInstruction* current); diff --git a/src/x64/macro-assembler-x64.cc b/src/x64/macro-assembler-x64.cc index b51d531e..9cfc9b65 100644 --- a/src/x64/macro-assembler-x64.cc +++ b/src/x64/macro-assembler-x64.cc @@ -2415,7 +2415,7 @@ void MacroAssembler::PushTryHandler(CodeLocation try_location, } // Save the current handler. Operand handler_operand = - ExternalOperand(ExternalReference(Isolate::k_handler_address, isolate())); + ExternalOperand(ExternalReference(Isolate::kHandlerAddress, isolate())); push(handler_operand); // Link this handler. movq(handler_operand, rsp); @@ -2426,7 +2426,7 @@ void MacroAssembler::PopTryHandler() { ASSERT_EQ(0, StackHandlerConstants::kNextOffset); // Unlink this handler. Operand handler_operand = - ExternalOperand(ExternalReference(Isolate::k_handler_address, isolate())); + ExternalOperand(ExternalReference(Isolate::kHandlerAddress, isolate())); pop(handler_operand); // Remove the remaining fields. addq(rsp, Immediate(StackHandlerConstants::kSize - kPointerSize)); @@ -2446,7 +2446,7 @@ void MacroAssembler::Throw(Register value) { movq(rax, value); } - ExternalReference handler_address(Isolate::k_handler_address, isolate()); + ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); Operand handler_operand = ExternalOperand(handler_address); movq(rsp, handler_operand); // get next in chain @@ -2482,7 +2482,7 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, movq(rax, value); } // Fetch top stack handler. - ExternalReference handler_address(Isolate::k_handler_address, isolate()); + ExternalReference handler_address(Isolate::kHandlerAddress, isolate()); Load(rsp, handler_address); // Unwind the handlers until the ENTRY handler is found. @@ -2505,12 +2505,12 @@ void MacroAssembler::ThrowUncatchable(UncatchableExceptionType type, if (type == OUT_OF_MEMORY) { // Set external caught exception to false. ExternalReference external_caught( - Isolate::k_external_caught_exception_address, isolate()); + Isolate::kExternalCaughtExceptionAddress, isolate()); Set(rax, static_cast<int64_t>(false)); Store(external_caught, rax); // Set pending exception and rax to out of memory exception. - ExternalReference pending_exception(Isolate::k_pending_exception_address, + ExternalReference pending_exception(Isolate::kPendingExceptionAddress, isolate()); movq(rax, Failure::OutOfMemoryException(), RelocInfo::NONE); Store(pending_exception, rax); @@ -2567,7 +2567,7 @@ void MacroAssembler::CmpInstanceType(Register map, InstanceType type) { void MacroAssembler::CheckFastElements(Register map, Label* fail, Label::Distance distance) { - STATIC_ASSERT(JSObject::FAST_ELEMENTS == 0); + STATIC_ASSERT(FAST_ELEMENTS == 0); cmpb(FieldOperand(map, Map::kBitField2Offset), Immediate(Map::kMaximumBitField2FastElementValue)); j(above, fail, distance); @@ -3041,8 +3041,8 @@ void MacroAssembler::EnterExitFramePrologue(bool save_rax) { movq(r14, rax); // Backup rax in callee-save register. } - Store(ExternalReference(Isolate::k_c_entry_fp_address, isolate()), rbp); - Store(ExternalReference(Isolate::k_context_address, isolate()), rsi); + Store(ExternalReference(Isolate::kCEntryFPAddress, isolate()), rbp); + Store(ExternalReference(Isolate::kContextAddress, isolate()), rsi); } @@ -3132,7 +3132,7 @@ void MacroAssembler::LeaveApiExitFrame() { void MacroAssembler::LeaveExitFrameEpilogue() { // Restore current context from top and clear it in debug mode. - ExternalReference context_address(Isolate::k_context_address, isolate()); + ExternalReference context_address(Isolate::kContextAddress, isolate()); Operand context_operand = ExternalOperand(context_address); movq(rsi, context_operand); #ifdef DEBUG @@ -3140,7 +3140,7 @@ void MacroAssembler::LeaveExitFrameEpilogue() { #endif // Clear the top frame. - ExternalReference c_entry_fp_address(Isolate::k_c_entry_fp_address, + ExternalReference c_entry_fp_address(Isolate::kCEntryFPAddress, isolate()); Operand c_entry_fp_operand = ExternalOperand(c_entry_fp_address); movq(c_entry_fp_operand, Immediate(0)); @@ -3303,7 +3303,7 @@ void MacroAssembler::LoadFromNumberDictionary(Label* miss, NumberDictionary::kElementsStartOffset + 2 * kPointerSize; ASSERT_EQ(NORMAL, 0); Test(FieldOperand(elements, r2, times_pointer_size, kDetailsOffset), - Smi::FromInt(PropertyDetails::TypeField::mask())); + Smi::FromInt(PropertyDetails::TypeField::kMask)); j(not_zero, miss); // Get the value at the masked, scaled index. @@ -3623,7 +3623,7 @@ void MacroAssembler::AllocateAsciiString(Register result, } -void MacroAssembler::AllocateConsString(Register result, +void MacroAssembler::AllocateTwoByteConsString(Register result, Register scratch1, Register scratch2, Label* gc_required) { @@ -3659,6 +3659,42 @@ void MacroAssembler::AllocateAsciiConsString(Register result, } +void MacroAssembler::AllocateTwoByteSlicedString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required) { + // Allocate heap number in new space. + AllocateInNewSpace(SlicedString::kSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + // Set the map. The other fields are left uninitialized. + LoadRoot(kScratchRegister, Heap::kSlicedStringMapRootIndex); + movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister); +} + + +void MacroAssembler::AllocateAsciiSlicedString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required) { + // Allocate heap number in new space. + AllocateInNewSpace(SlicedString::kSize, + result, + scratch1, + scratch2, + gc_required, + TAG_OBJECT); + + // Set the map. The other fields are left uninitialized. + LoadRoot(kScratchRegister, Heap::kSlicedAsciiStringMapRootIndex); + movq(FieldOperand(result, HeapObject::kMapOffset), kScratchRegister); +} + + // Copy memory, byte-by-byte, from source to destination. Not optimized for // long or aligned copies. The contents of scratch and length are destroyed. // Destination is incremented by length, source, length and scratch are diff --git a/src/x64/macro-assembler-x64.h b/src/x64/macro-assembler-x64.h index 47ce01bd..e7eb104c 100644 --- a/src/x64/macro-assembler-x64.h +++ b/src/x64/macro-assembler-x64.h @@ -921,7 +921,7 @@ class MacroAssembler: public Assembler { // Allocate a raw cons string object. Only the map field of the result is // initialized. - void AllocateConsString(Register result, + void AllocateTwoByteConsString(Register result, Register scratch1, Register scratch2, Label* gc_required); @@ -930,6 +930,17 @@ class MacroAssembler: public Assembler { Register scratch2, Label* gc_required); + // Allocate a raw sliced string object. Only the map field of the result is + // initialized. + void AllocateTwoByteSlicedString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required); + void AllocateAsciiSlicedString(Register result, + Register scratch1, + Register scratch2, + Label* gc_required); + // --------------------------------------------------------------------------- // Support functions. diff --git a/src/x64/stub-cache-x64.cc b/src/x64/stub-cache-x64.cc index 5ea72579..76d25557 100644 --- a/src/x64/stub-cache-x64.cc +++ b/src/x64/stub-cache-x64.cc @@ -2537,7 +2537,7 @@ MaybeObject* KeyedStoreStubCompiler::CompileStoreElement(Map* receiver_map) { // -- rsp[0] : return address // ----------------------------------- Code* stub; - JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + ElementsKind elements_kind = receiver_map->elements_kind(); bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE; MaybeObject* maybe_stub = KeyedStoreElementStub(is_js_array, elements_kind).TryGetCode(); @@ -2996,7 +2996,7 @@ MaybeObject* KeyedLoadStubCompiler::CompileLoadElement(Map* receiver_map) { // -- rsp[0] : return address // ----------------------------------- Code* stub; - JSObject::ElementsKind elements_kind = receiver_map->elements_kind(); + ElementsKind elements_kind = receiver_map->elements_kind(); MaybeObject* maybe_stub = KeyedLoadElementStub(elements_kind).TryGetCode(); if (!maybe_stub->To(&stub)) return maybe_stub; __ DispatchMap(rdx, @@ -3227,7 +3227,7 @@ void KeyedLoadStubCompiler::GenerateLoadDictionaryElement( void KeyedLoadStubCompiler::GenerateLoadExternalArray( MacroAssembler* masm, - JSObject::ElementsKind elements_kind) { + ElementsKind elements_kind) { // ----------- S t a t e ------------- // -- rax : key // -- rdx : receiver @@ -3255,29 +3255,29 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ movq(rbx, FieldOperand(rbx, ExternalArray::kExternalPointerOffset)); // rbx: base pointer of external storage switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: __ movsxbq(rcx, Operand(rbx, rcx, times_1, 0)); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ movzxbq(rcx, Operand(rbx, rcx, times_1, 0)); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: __ movsxwq(rcx, Operand(rbx, rcx, times_2, 0)); break; - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ movzxwq(rcx, Operand(rbx, rcx, times_2, 0)); break; - case JSObject::EXTERNAL_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: __ movsxlq(rcx, Operand(rbx, rcx, times_4, 0)); break; - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ movl(rcx, Operand(rbx, rcx, times_4, 0)); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: __ cvtss2sd(xmm0, Operand(rbx, rcx, times_4, 0)); break; - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: __ movsd(xmm0, Operand(rbx, rcx, times_8, 0)); break; default: @@ -3293,7 +3293,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( // xmm0: value as double. ASSERT(kSmiValueSize == 32); - if (elements_kind == JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS) { + if (elements_kind == EXTERNAL_UNSIGNED_INT_ELEMENTS) { // For the UnsignedInt array type, we need to see whether // the value can be represented in a Smi. If not, we need to convert // it to a HeapNumber. @@ -3317,8 +3317,8 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( __ movsd(FieldOperand(rcx, HeapNumber::kValueOffset), xmm0); __ movq(rax, rcx); __ ret(0); - } else if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS || - elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_FLOAT_ELEMENTS || + elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { // For the floating-point array type, we need to always allocate a // HeapNumber. __ AllocateHeapNumber(rcx, rbx, &slow); @@ -3361,7 +3361,7 @@ void KeyedLoadStubCompiler::GenerateLoadExternalArray( void KeyedStoreStubCompiler::GenerateStoreExternalArray( MacroAssembler* masm, - JSObject::ElementsKind elements_kind) { + ElementsKind elements_kind) { // ----------- S t a t e ------------- // -- rax : value // -- rcx : key @@ -3391,7 +3391,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // rbx: elements array // rdi: untagged key Label check_heap_number; - if (elements_kind == JSObject::EXTERNAL_PIXEL_ELEMENTS) { + if (elements_kind == EXTERNAL_PIXEL_ELEMENTS) { // Float to pixel conversion is only implemented in the runtime for now. __ JumpIfNotSmi(rax, &slow); } else { @@ -3402,7 +3402,7 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( __ movq(rbx, FieldOperand(rbx, ExternalArray::kExternalPointerOffset)); // rbx: base pointer of external storage switch (elements_kind) { - case JSObject::EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: { // Clamp the value to [0..255]. Label done; __ testl(rdx, Immediate(0xFFFFFF00)); @@ -3413,39 +3413,39 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( } __ movb(Operand(rbx, rdi, times_1, 0), rdx); break; - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ movb(Operand(rbx, rdi, times_1, 0), rdx); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ movw(Operand(rbx, rdi, times_2, 0), rdx); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: __ movl(Operand(rbx, rdi, times_4, 0), rdx); break; - case JSObject::EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: // Need to perform int-to-float conversion. __ cvtlsi2ss(xmm0, rdx); __ movss(Operand(rbx, rdi, times_4, 0), xmm0); break; - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: // Need to perform int-to-float conversion. __ cvtlsi2sd(xmm0, rdx); __ movsd(Operand(rbx, rdi, times_8, 0), xmm0); break; - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } __ ret(0); // TODO(danno): handle heap number -> pixel array conversion - if (elements_kind != JSObject::EXTERNAL_PIXEL_ELEMENTS) { + if (elements_kind != EXTERNAL_PIXEL_ELEMENTS) { __ bind(&check_heap_number); // rax: value // rcx: key (a smi) @@ -3464,11 +3464,11 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // rdi: untagged index // rbx: base pointer of external storage // top of FPU stack: value - if (elements_kind == JSObject::EXTERNAL_FLOAT_ELEMENTS) { + if (elements_kind == EXTERNAL_FLOAT_ELEMENTS) { __ cvtsd2ss(xmm0, xmm0); __ movss(Operand(rbx, rdi, times_4, 0), xmm0); __ ret(0); - } else if (elements_kind == JSObject::EXTERNAL_DOUBLE_ELEMENTS) { + } else if (elements_kind == EXTERNAL_DOUBLE_ELEMENTS) { __ movsd(Operand(rbx, rdi, times_8, 0), xmm0); __ ret(0); } else { @@ -3482,30 +3482,30 @@ void KeyedStoreStubCompiler::GenerateStoreExternalArray( // rdi: untagged index // rbx: base pointer of external storage switch (elements_kind) { - case JSObject::EXTERNAL_BYTE_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_BYTE_ELEMENTS: + case EXTERNAL_BYTE_ELEMENTS: + case EXTERNAL_UNSIGNED_BYTE_ELEMENTS: __ cvttsd2si(rdx, xmm0); __ movb(Operand(rbx, rdi, times_1, 0), rdx); break; - case JSObject::EXTERNAL_SHORT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_SHORT_ELEMENTS: + case EXTERNAL_SHORT_ELEMENTS: + case EXTERNAL_UNSIGNED_SHORT_ELEMENTS: __ cvttsd2si(rdx, xmm0); __ movw(Operand(rbx, rdi, times_2, 0), rdx); break; - case JSObject::EXTERNAL_INT_ELEMENTS: - case JSObject::EXTERNAL_UNSIGNED_INT_ELEMENTS: + case EXTERNAL_INT_ELEMENTS: + case EXTERNAL_UNSIGNED_INT_ELEMENTS: // Convert to int64, so that NaN and infinities become // 0x8000000000000000, which is zero mod 2^32. __ cvttsd2siq(rdx, xmm0); __ movl(Operand(rbx, rdi, times_4, 0), rdx); break; - case JSObject::EXTERNAL_PIXEL_ELEMENTS: - case JSObject::EXTERNAL_FLOAT_ELEMENTS: - case JSObject::EXTERNAL_DOUBLE_ELEMENTS: - case JSObject::FAST_ELEMENTS: - case JSObject::FAST_DOUBLE_ELEMENTS: - case JSObject::DICTIONARY_ELEMENTS: - case JSObject::NON_STRICT_ARGUMENTS_ELEMENTS: + case EXTERNAL_PIXEL_ELEMENTS: + case EXTERNAL_FLOAT_ELEMENTS: + case EXTERNAL_DOUBLE_ELEMENTS: + case FAST_ELEMENTS: + case FAST_DOUBLE_ELEMENTS: + case DICTIONARY_ELEMENTS: + case NON_STRICT_ARGUMENTS_ELEMENTS: UNREACHABLE(); break; } diff --git a/src/zone-inl.h b/src/zone-inl.h index 6e2d558a..4870105f 100644 --- a/src/zone-inl.h +++ b/src/zone-inl.h @@ -55,7 +55,12 @@ inline void* Zone::New(int size) { // Check if the requested size is available without expanding. Address result = position_; - if ((position_ += size) > limit_) result = NewExpand(size); + + if (size > limit_ - position_) { + result = NewExpand(size); + } else { + position_ += size; + } // Check that the result has the proper alignment and return it. ASSERT(IsAddressAligned(result, kAlignment, 0)); diff --git a/src/zone.cc b/src/zone.cc index 7574778f..2d14d137 100644 --- a/src/zone.cc +++ b/src/zone.cc @@ -168,7 +168,7 @@ Address Zone::NewExpand(int size) { // Make sure the requested size is already properly aligned and that // there isn't enough room in the Zone to satisfy the request. ASSERT(size == RoundDown(size, kAlignment)); - ASSERT(position_ + size > limit_); + ASSERT(size > limit_ - position_); // Compute the new segment size. We use a 'high water mark' // strategy, where we increase the segment size every time we expand @@ -177,7 +177,13 @@ Address Zone::NewExpand(int size) { Segment* head = segment_head_; int old_size = (head == NULL) ? 0 : head->size(); static const int kSegmentOverhead = sizeof(Segment) + kAlignment; - int new_size = kSegmentOverhead + size + (old_size << 1); + int new_size_no_overhead = size + (old_size << 1); + int new_size = kSegmentOverhead + new_size_no_overhead; + // Guard against integer overflow. + if (new_size_no_overhead < size || new_size < kSegmentOverhead) { + V8::FatalProcessOutOfMemory("Zone"); + return NULL; + } if (new_size < kMinimumSegmentSize) { new_size = kMinimumSegmentSize; } else if (new_size > kMaximumSegmentSize) { @@ -196,6 +202,11 @@ Address Zone::NewExpand(int size) { // Recompute 'top' and 'limit' based on the new segment. Address result = RoundUp(segment->start(), kAlignment); position_ = result + size; + // Check for address overflow. + if (position_ < result) { + V8::FatalProcessOutOfMemory("Zone"); + return NULL; + } limit_ = segment->end(); ASSERT(position_ <= limit_); return result; |