}
+void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
+ ASSERT_EQ(1, args->length());
+
+ Load(args->at(0));
+ frame_->PopToR0();
+ {
+ VirtualFrame::SpilledScope spilled_scope(frame_);
+
+ Label done;
+ Label call_runtime;
+ __ BranchOnSmi(r0, &done);
+
+ // Load JSRegExp map into r1. Check that argument object has this map.
+ // Arguments to this function should be results of calling RegExp exec,
+ // which is either an unmodified JSRegExpResult or null. Anything not having
+ // the unmodified JSRegExpResult map is returned unmodified.
+ // This also ensures that elements are fast.
+
+ __ ldr(r1, ContextOperand(cp, Context::GLOBAL_INDEX));
+ __ ldr(r1, FieldMemOperand(r1, GlobalObject::kGlobalContextOffset));
+ __ ldr(r1, ContextOperand(r1, Context::REGEXP_RESULT_MAP_INDEX));
+ __ ldr(ip, FieldMemOperand(r0, HeapObject::kMapOffset));
+ __ cmp(r1, Operand(ip));
+ __ b(ne, &done);
+
+ // All set, copy the contents to a new object.
+ __ AllocateInNewSpace(JSRegExpResult::kSize,
+ r2,
+ r3,
+ r4,
+ &call_runtime,
+ NO_ALLOCATION_FLAGS);
+ // Store RegExpResult map as map of allocated object.
+ ASSERT(JSRegExpResult::kSize == 6 * kPointerSize);
+ // Copy all fields (map is already in r1) from (untagged) r0 to r2.
+ // Change map of elements array (ends up in r4) to be a FixedCOWArray.
+ __ bic(r0, r0, Operand(kHeapObjectTagMask));
+ __ ldm(ib, r0, r3.bit() | r4.bit() | r5.bit() | r6.bit() | r7.bit());
+ __ stm(ia, r2,
+ r1.bit() | r3.bit() | r4.bit() | r5.bit() | r6.bit() | r7.bit());
+ ASSERT(!Heap::InNewSpace(Heap::fixed_cow_array_map()));
+ ASSERT(JSRegExp::kElementsOffset == 2 * kPointerSize);
+ // Check whether elements array is empty fixed array, and otherwise make
+ // it copy-on-write (it never should be empty unless someone is messing
+ // with the arguments to the runtime function).
+ __ LoadRoot(ip, Heap::kEmptyFixedArrayRootIndex);
+ __ add(r0, r2, Operand(kHeapObjectTag)); // Tag result and move it to r0.
+ __ cmp(r4, ip);
+ __ b(eq, &done);
+ __ LoadRoot(ip, Heap::kFixedCOWArrayMapRootIndex);
+ __ str(ip, FieldMemOperand(r4, HeapObject::kMapOffset));
+ __ b(&done);
+ __ bind(&call_runtime);
+ __ push(r0);
+ __ CallRuntime(Runtime::kRegExpCloneResult, 1);
+ __ bind(&done);
+ }
+ frame_->EmitPush(r0);
+}
+
+
class DeferredSearchCache: public DeferredCode {
public:
DeferredSearchCache(Register dst, Register cache, Register key)
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
+ void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
+
// Support for fast native caches.
void GenerateGetFromCache(ZoneList<Expression*>* args);
}
+void FullCodeGenerator::EmitRegExpCloneResult(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ VisitForValue(args->at(0), kStack);
+ __ CallRuntime(Runtime::kRegExpConstructResult, 1);
+ Apply(context_, r0);
+}
+
+
void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
ASSERT(args->length() == 3);
VisitForValue(args->at(0), kStack);
// the special case of accessing the PC register.
int32_t Simulator::get_register(int reg) const {
ASSERT((reg >= 0) && (reg < num_registers));
+ // Stupid code added to avoid bug in GCC.
+ // See: http://gcc.gnu.org/bugzilla/show_bug.cgi?id=43949
+ if (reg >= num_registers) return 0;
+ // End stupid code.
return registers_[reg] + ((reg == pc) ? Instr::kPCReadOffset : 0);
}
}
case 3: {
// Print("ib");
- UNIMPLEMENTED();
+ start_address = rn_val + 4;
+ end_address = rn_val + (num_regs * 4);
+ rn_val = end_address;
break;
}
default: {
F(StringCompare, 2, 1) \
F(RegExpExec, 4, 1) \
F(RegExpConstructResult, 3, 1) \
+ F(RegExpCloneResult, 1, 1) \
F(GetFromCache, 2, 1) \
F(NumberToString, 1, 1) \
F(SwapElements, 3, 1) \
void FullCodeGenerator::EmitInlineRuntimeCall(CallRuntime* expr) {
Handle<String> name = expr->name();
- if (strcmp("_IsSmi", *name->ToCString()) == 0) {
- EmitIsSmi(expr->arguments());
- } else if (strcmp("_IsNonNegativeSmi", *name->ToCString()) == 0) {
- EmitIsNonNegativeSmi(expr->arguments());
- } else if (strcmp("_IsObject", *name->ToCString()) == 0) {
- EmitIsObject(expr->arguments());
- } else if (strcmp("_IsSpecObject", *name->ToCString()) == 0) {
- EmitIsSpecObject(expr->arguments());
- } else if (strcmp("_IsUndetectableObject", *name->ToCString()) == 0) {
- EmitIsUndetectableObject(expr->arguments());
- } else if (strcmp("_IsFunction", *name->ToCString()) == 0) {
- EmitIsFunction(expr->arguments());
- } else if (strcmp("_IsArray", *name->ToCString()) == 0) {
- EmitIsArray(expr->arguments());
- } else if (strcmp("_IsRegExp", *name->ToCString()) == 0) {
- EmitIsRegExp(expr->arguments());
- } else if (strcmp("_IsConstructCall", *name->ToCString()) == 0) {
- EmitIsConstructCall(expr->arguments());
- } else if (strcmp("_ObjectEquals", *name->ToCString()) == 0) {
- EmitObjectEquals(expr->arguments());
- } else if (strcmp("_Arguments", *name->ToCString()) == 0) {
- EmitArguments(expr->arguments());
- } else if (strcmp("_ArgumentsLength", *name->ToCString()) == 0) {
- EmitArgumentsLength(expr->arguments());
- } else if (strcmp("_ClassOf", *name->ToCString()) == 0) {
- EmitClassOf(expr->arguments());
- } else if (strcmp("_Log", *name->ToCString()) == 0) {
- EmitLog(expr->arguments());
- } else if (strcmp("_RandomHeapNumber", *name->ToCString()) == 0) {
- EmitRandomHeapNumber(expr->arguments());
- } else if (strcmp("_SubString", *name->ToCString()) == 0) {
- EmitSubString(expr->arguments());
- } else if (strcmp("_RegExpExec", *name->ToCString()) == 0) {
- EmitRegExpExec(expr->arguments());
- } else if (strcmp("_ValueOf", *name->ToCString()) == 0) {
- EmitValueOf(expr->arguments());
- } else if (strcmp("_SetValueOf", *name->ToCString()) == 0) {
- EmitSetValueOf(expr->arguments());
- } else if (strcmp("_NumberToString", *name->ToCString()) == 0) {
- EmitNumberToString(expr->arguments());
- } else if (strcmp("_StringCharFromCode", *name->ToCString()) == 0) {
- EmitStringCharFromCode(expr->arguments());
- } else if (strcmp("_StringCharCodeAt", *name->ToCString()) == 0) {
- EmitStringCharCodeAt(expr->arguments());
- } else if (strcmp("_StringCharAt", *name->ToCString()) == 0) {
- EmitStringCharAt(expr->arguments());
- } else if (strcmp("_StringAdd", *name->ToCString()) == 0) {
- EmitStringAdd(expr->arguments());
- } else if (strcmp("_StringCompare", *name->ToCString()) == 0) {
- EmitStringCompare(expr->arguments());
- } else if (strcmp("_MathPow", *name->ToCString()) == 0) {
- EmitMathPow(expr->arguments());
- } else if (strcmp("_MathSin", *name->ToCString()) == 0) {
- EmitMathSin(expr->arguments());
- } else if (strcmp("_MathCos", *name->ToCString()) == 0) {
- EmitMathCos(expr->arguments());
- } else if (strcmp("_MathSqrt", *name->ToCString()) == 0) {
- EmitMathSqrt(expr->arguments());
- } else if (strcmp("_CallFunction", *name->ToCString()) == 0) {
- EmitCallFunction(expr->arguments());
- } else if (strcmp("_RegExpConstructResult", *name->ToCString()) == 0) {
- EmitRegExpConstructResult(expr->arguments());
- } else if (strcmp("_SwapElements", *name->ToCString()) == 0) {
- EmitSwapElements(expr->arguments());
- } else if (strcmp("_GetFromCache", *name->ToCString()) == 0) {
- EmitGetFromCache(expr->arguments());
- } else if (strcmp("_IsRegExpEquivalent", *name->ToCString()) == 0) {
- EmitIsRegExpEquivalent(expr->arguments());
- } else if (strcmp("_IsStringWrapperSafeForDefaultValueOf",
- *name->ToCString()) == 0) {
- EmitIsStringWrapperSafeForDefaultValueOf(expr->arguments());
- } else {
- UNREACHABLE();
+ SmartPointer<char> cstring = name->ToCString();
+
+#define CHECK_EMIT_INLINE_CALL(name, x, y) \
+ if (strcmp("_"#name, *cstring) == 0) { \
+ Emit##name(expr->arguments()); \
+ return; \
}
+
+ INLINE_RUNTIME_FUNCTION_LIST(CHECK_EMIT_INLINE_CALL)
+ UNREACHABLE();
+#undef CHECK_EMIT_INLINE_CALL
}
// Platform-specific code for inline runtime calls.
void EmitInlineRuntimeCall(CallRuntime* expr);
- void EmitIsSmi(ZoneList<Expression*>* arguments);
- void EmitIsNonNegativeSmi(ZoneList<Expression*>* arguments);
- void EmitIsObject(ZoneList<Expression*>* arguments);
- void EmitIsSpecObject(ZoneList<Expression*>* arguments);
- void EmitIsUndetectableObject(ZoneList<Expression*>* arguments);
- void EmitIsFunction(ZoneList<Expression*>* arguments);
- void EmitIsArray(ZoneList<Expression*>* arguments);
- void EmitIsRegExp(ZoneList<Expression*>* arguments);
- void EmitIsConstructCall(ZoneList<Expression*>* arguments);
- void EmitIsStringWrapperSafeForDefaultValueOf(
- ZoneList<Expression*>* arguments);
- void EmitObjectEquals(ZoneList<Expression*>* arguments);
- void EmitArguments(ZoneList<Expression*>* arguments);
- void EmitArgumentsLength(ZoneList<Expression*>* arguments);
- void EmitClassOf(ZoneList<Expression*>* arguments);
- void EmitValueOf(ZoneList<Expression*>* arguments);
- void EmitSetValueOf(ZoneList<Expression*>* arguments);
- void EmitNumberToString(ZoneList<Expression*>* arguments);
- void EmitStringCharFromCode(ZoneList<Expression*>* arguments);
- void EmitStringCharCodeAt(ZoneList<Expression*>* arguments);
- void EmitStringCharAt(ZoneList<Expression*>* arguments);
- void EmitStringCompare(ZoneList<Expression*>* arguments);
- void EmitStringAdd(ZoneList<Expression*>* arguments);
- void EmitLog(ZoneList<Expression*>* arguments);
- void EmitRandomHeapNumber(ZoneList<Expression*>* arguments);
- void EmitSubString(ZoneList<Expression*>* arguments);
- void EmitRegExpExec(ZoneList<Expression*>* arguments);
- void EmitMathPow(ZoneList<Expression*>* arguments);
- void EmitMathSin(ZoneList<Expression*>* arguments);
- void EmitMathCos(ZoneList<Expression*>* arguments);
- void EmitMathSqrt(ZoneList<Expression*>* arguments);
- void EmitCallFunction(ZoneList<Expression*>* arguments);
- void EmitRegExpConstructResult(ZoneList<Expression*>* arguments);
- void EmitSwapElements(ZoneList<Expression*>* arguments);
- void EmitGetFromCache(ZoneList<Expression*>* arguments);
- void EmitIsRegExpEquivalent(ZoneList<Expression*>* arguments);
+
+#define EMIT_INLINE_RUNTIME_CALL(name, x, y) \
+ void Emit##name(ZoneList<Expression*>* arguments);
+ INLINE_RUNTIME_FUNCTION_LIST(EMIT_INLINE_RUNTIME_CALL)
+#undef EMIT_INLINE_RUNTIME_CALL
// Platform-specific code for loading variables.
void EmitVariableLoad(Variable* expr, Expression::Context context);
class DeferredAllocateInNewSpace: public DeferredCode {
public:
- DeferredAllocateInNewSpace(int size, Register target)
- : size_(size), target_(target) {
+ DeferredAllocateInNewSpace(int size,
+ Register target,
+ int registers_to_save = 0)
+ : size_(size), target_(target), registers_to_save_(registers_to_save) {
ASSERT(size >= kPointerSize && size <= Heap::MaxObjectSizeInNewSpace());
+ ASSERT_EQ(0, registers_to_save & target.bit());
set_comment("[ DeferredAllocateInNewSpace");
}
void Generate();
private:
int size_;
Register target_;
+ int registers_to_save_;
};
void DeferredAllocateInNewSpace::Generate() {
+ for (int i = 0; i < kNumRegs; i++) {
+ if (registers_to_save_ & (1 << i)) {
+ Register save_register = { i };
+ __ push(save_register);
+ }
+ }
__ push(Immediate(Smi::FromInt(size_)));
__ CallRuntime(Runtime::kAllocateInNewSpace, 1);
if (!target_.is(eax)) {
__ mov(target_, eax);
}
+ for (int i = kNumRegs - 1; i >= 0; i--) {
+ if (registers_to_save_ & (1 << i)) {
+ Register save_register = { i };
+ __ pop(save_register);
+ }
+ }
}
}
+void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
+ ASSERT_EQ(1, args->length());
+
+ Load(args->at(0));
+ Result object_result = frame_->Pop();
+ object_result.ToRegister(eax);
+ object_result.Unuse();
+ {
+ VirtualFrame::SpilledScope spilled_scope;
+
+ Label done;
+
+ __ test(eax, Immediate(kSmiTagMask));
+ __ j(zero, &done);
+
+ // Load JSRegExpResult map into edx.
+ // Arguments to this function should be results of calling RegExp exec,
+ // which is either an unmodified JSRegExpResult or null. Anything not having
+ // the unmodified JSRegExpResult map is returned unmodified.
+ // This also ensures that elements are fast.
+ __ mov(edx, ContextOperand(esi, Context::GLOBAL_INDEX));
+ __ mov(edx, FieldOperand(edx, GlobalObject::kGlobalContextOffset));
+ __ mov(edx, ContextOperand(edx, Context::REGEXP_RESULT_MAP_INDEX));
+ __ cmp(edx, FieldOperand(eax, HeapObject::kMapOffset));
+ __ j(not_equal, &done);
+
+ if (FLAG_debug_code) {
+ // Check that object really has empty properties array, as the map
+ // should guarantee.
+ __ cmp(FieldOperand(eax, JSObject::kPropertiesOffset),
+ Immediate(Factory::empty_fixed_array()));
+ __ Check(equal, "JSRegExpResult: default map but non-empty properties.");
+ }
+
+ DeferredAllocateInNewSpace* allocate_fallback =
+ new DeferredAllocateInNewSpace(JSRegExpResult::kSize,
+ ebx,
+ edx.bit() | eax.bit());
+
+ // All set, copy the contents to a new object.
+ __ AllocateInNewSpace(JSRegExpResult::kSize,
+ ebx,
+ ecx,
+ no_reg,
+ allocate_fallback->entry_label(),
+ TAG_OBJECT);
+ __ bind(allocate_fallback->exit_label());
+
+ // Copy all fields from eax to ebx.
+ STATIC_ASSERT(JSRegExpResult::kSize % (2 * kPointerSize) == 0);
+ // There is an even number of fields, so unroll the loop once
+ // for efficiency.
+ for (int i = 0; i < JSRegExpResult::kSize; i += 2 * kPointerSize) {
+ STATIC_ASSERT(JSObject::kMapOffset % (2 * kPointerSize) == 0);
+ if (i != JSObject::kMapOffset) {
+ // The map was already loaded into edx.
+ __ mov(edx, FieldOperand(eax, i));
+ }
+ __ mov(ecx, FieldOperand(eax, i + kPointerSize));
+
+ STATIC_ASSERT(JSObject::kElementsOffset % (2 * kPointerSize) == 0);
+ if (i == JSObject::kElementsOffset) {
+ // If the elements array isn't empty, make it copy-on-write
+ // before copying it.
+ Label empty;
+ __ cmp(Operand(edx), Immediate(Factory::empty_fixed_array()));
+ __ j(equal, &empty);
+ ASSERT(!Heap::InNewSpace(Heap::fixed_cow_array_map()));
+ __ mov(FieldOperand(edx, HeapObject::kMapOffset),
+ Immediate(Factory::fixed_cow_array_map()));
+ __ bind(&empty);
+ }
+ __ mov(FieldOperand(ebx, i), edx);
+ __ mov(FieldOperand(ebx, i + kPointerSize), ecx);
+ }
+ __ mov(eax, ebx);
+
+ __ bind(&done);
+ }
+ frame_->Push(eax);
+}
+
+
class DeferredSearchCache: public DeferredCode {
public:
DeferredSearchCache(Register dst, Register cache, Register key)
// Support for direct calls from JavaScript to native RegExp code.
void GenerateRegExpExec(ZoneList<Expression*>* args);
+ // Construct a RegExp exec result with two in-object properties.
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
+ // Clone the result of a regexp function.
+ // Must be an object created by GenerateRegExpConstructResult with
+ // no extra properties.
+ void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
+
// Support for fast native caches.
void GenerateGetFromCache(ZoneList<Expression*>* args);
}
+void FullCodeGenerator::EmitRegExpCloneResult(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ VisitForValue(args->at(0), kStack);
+ __ CallRuntime(Runtime::kRegExpCloneResult, 1);
+ Apply(context_, eax);
+}
+
+
void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
ASSERT(args->length() == 3);
VisitForValue(args->at(0), kStack);
var regExpCache = new RegExpCache();
-function CloneRegExpResult(array) {
- if (array == null) return null;
- var length = array.length;
- var answer = %_RegExpConstructResult(length, array.index, array.input);
- for (var i = 0; i < length; i++) {
- answer[i] = array[i];
- }
- return answer;
-}
-
-
function BuildResultFromMatchInfo(lastMatchInfo, s) {
var numResults = NUMBER_OF_CAPTURES(lastMatchInfo) >> 1;
var result = %_RegExpConstructResult(numResults, lastMatchInfo[CAPTURE0], s);
%_IsRegExpEquivalent(cache.regExp, this) &&
%_ObjectEquals(cache.subject, string)) {
if (cache.answerSaved) {
- return CloneRegExpResult(cache.answer);
+ return %_RegExpCloneResult(cache.answer);
} else {
saveAnswer = true;
}
cache.regExp = this;
cache.subject = s;
cache.lastIndex = lastIndex;
- if (saveAnswer) cache.answer = CloneRegExpResult(result);
+ if (saveAnswer) cache.answer = %_RegExpCloneResult(result);
cache.answerSaved = saveAnswer;
cache.type = 'exec';
}
}
+static Object* Runtime_RegExpCloneResult(Arguments args) {
+ ASSERT(args.length() == 1);
+ Map* regexp_result_map;
+ {
+ AssertNoAllocation no_gc;
+ HandleScope handles;
+ regexp_result_map = Top::global_context()->regexp_result_map();
+ }
+ if (!args[0]->IsJSArray()) return args[0];
+
+ JSArray* result = JSArray::cast(args[0]);
+ // Arguments to RegExpCloneResult should always be fresh RegExp exec call
+ // results (either a fresh JSRegExpResult or null).
+ // If the argument is not a JSRegExpResult, or isn't unmodified, just return
+ // the argument uncloned.
+ if (result->map() != regexp_result_map) return result;
+
+ // Having the original JSRegExpResult map guarantees that we have
+ // fast elements and no properties except the two in-object properties.
+ ASSERT(result->HasFastElements());
+ ASSERT(result->properties() == Heap::empty_fixed_array());
+ ASSERT_EQ(2, regexp_result_map->inobject_properties());
+
+ Object* new_array_alloc = Heap::AllocateRaw(JSRegExpResult::kSize,
+ NEW_SPACE,
+ OLD_POINTER_SPACE);
+ if (new_array_alloc->IsFailure()) return new_array_alloc;
+
+ // Set HeapObject map to JSRegExpResult map.
+ reinterpret_cast<HeapObject*>(new_array_alloc)->set_map(regexp_result_map);
+
+ JSArray* new_array = JSArray::cast(new_array_alloc);
+
+ // Copy JSObject properties.
+ new_array->set_properties(result->properties()); // Empty FixedArray.
+
+ // Copy JSObject elements as copy-on-write.
+ FixedArray* elements = FixedArray::cast(result->elements());
+ if (elements != Heap::empty_fixed_array()) {
+ ASSERT(!Heap::InNewSpace(Heap::fixed_cow_array_map()));
+ // No write barrier is necessary when writing old-space pointer.
+ elements->set_map(Heap::fixed_cow_array_map());
+ }
+ new_array->set_elements(elements);
+
+ // Copy JSArray length.
+ new_array->set_length(result->length());
+
+ // Copy JSRegExpResult in-object property fields input and index.
+ new_array->FastPropertyAtPut(JSRegExpResult::kIndexIndex,
+ result->FastPropertyAt(
+ JSRegExpResult::kIndexIndex));
+ new_array->FastPropertyAtPut(JSRegExpResult::kInputIndex,
+ result->FastPropertyAt(
+ JSRegExpResult::kInputIndex));
+ return new_array;
+}
+
+
static Object* Runtime_RegExpInitializeObject(Arguments args) {
AssertNoAllocation no_alloc;
ASSERT(args.length() == 5);
F(RegExpExecMultiple, 4, 1) \
F(RegExpInitializeObject, 5, 1) \
F(RegExpConstructResult, 3, 1) \
+ F(RegExpCloneResult, 1, 1) \
\
/* Strings */ \
F(StringCharCodeAt, 2, 1) \
class DeferredAllocateInNewSpace: public DeferredCode {
public:
- DeferredAllocateInNewSpace(int size, Register target)
- : size_(size), target_(target) {
+ DeferredAllocateInNewSpace(int size,
+ Register target,
+ int registers_to_save = 0)
+ : size_(size), target_(target), registers_to_save_(registers_to_save) {
ASSERT(size >= kPointerSize && size <= Heap::MaxObjectSizeInNewSpace());
set_comment("[ DeferredAllocateInNewSpace");
}
private:
int size_;
Register target_;
+ int registers_to_save_;
};
void DeferredAllocateInNewSpace::Generate() {
+ for (int i = 0; i < kNumRegs; i++) {
+ if (registers_to_save_ & (1 << i)) {
+ Register save_register = { i };
+ __ push(save_register);
+ }
+ }
__ Push(Smi::FromInt(size_));
__ CallRuntime(Runtime::kAllocateInNewSpace, 1);
if (!target_.is(rax)) {
__ movq(target_, rax);
}
+ for (int i = kNumRegs - 1; i >= 0; i--) {
+ if (registers_to_save_ & (1 << i)) {
+ Register save_register = { i };
+ __ push(save_register);
+ }
+ }
}
}
+void CodeGenerator::GenerateRegExpCloneResult(ZoneList<Expression*>* args) {
+ ASSERT_EQ(1, args->length());
+
+ Load(args->at(0));
+ Result object_result = frame_->Pop();
+ object_result.ToRegister(rax);
+ object_result.Unuse();
+ {
+ VirtualFrame::SpilledScope spilled_scope;
+
+ Label done;
+ __ JumpIfSmi(rax, &done);
+
+ // Load JSRegExpResult map into rdx.
+ // Arguments to this function should be results of calling RegExp exec,
+ // which is either an unmodified JSRegExpResult or null. Anything not having
+ // the unmodified JSRegExpResult map is returned unmodified.
+ // This also ensures that elements are fast.
+
+ __ movq(rdx, ContextOperand(rsi, Context::GLOBAL_INDEX));
+ __ movq(rdx, FieldOperand(rdx, GlobalObject::kGlobalContextOffset));
+ __ movq(rdx, ContextOperand(rdx, Context::REGEXP_RESULT_MAP_INDEX));
+ __ cmpq(rdx, FieldOperand(rax, HeapObject::kMapOffset));
+ __ j(not_equal, &done);
+
+ DeferredAllocateInNewSpace* allocate_fallback =
+ new DeferredAllocateInNewSpace(JSRegExpResult::kSize,
+ rbx,
+ rdx.bit() | rax.bit());
+
+ // All set, copy the contents to a new object.
+ __ AllocateInNewSpace(JSRegExpResult::kSize,
+ rbx,
+ no_reg,
+ no_reg,
+ allocate_fallback->entry_label(),
+ TAG_OBJECT);
+ __ bind(allocate_fallback->exit_label());
+
+ STATIC_ASSERT(JSRegExpResult::kSize % (2 * kPointerSize) == 0);
+ // There is an even number of fields, so unroll the loop once
+ // for efficiency.
+ for (int i = 0; i < JSRegExpResult::kSize; i += 2 * kPointerSize) {
+ STATIC_ASSERT(JSObject::kMapOffset % (2 * kPointerSize) == 0);
+ if (i != JSObject::kMapOffset) {
+ // The map was already loaded into edx.
+ __ movq(rdx, FieldOperand(rax, i));
+ }
+ __ movq(rcx, FieldOperand(rax, i + kPointerSize));
+
+ STATIC_ASSERT(JSObject::kElementsOffset % (2 * kPointerSize) == 0);
+ if (i == JSObject::kElementsOffset) {
+ // If the elements array isn't empty, make it copy-on-write
+ // before copying it.
+ Label empty;
+ __ CompareRoot(rdx, Heap::kEmptyFixedArrayRootIndex);
+ __ j(equal, &empty);
+ ASSERT(!Heap::InNewSpace(Heap::fixed_cow_array_map()));
+ __ LoadRoot(kScratchRegister, Heap::kFixedCOWArrayMapRootIndex);
+ __ movq(FieldOperand(rdx, HeapObject::kMapOffset), kScratchRegister);
+ __ bind(&empty);
+ }
+ __ movq(FieldOperand(rbx, i), rdx);
+ __ movq(FieldOperand(rbx, i + kPointerSize), rcx);
+ }
+ __ movq(rax, rbx);
+
+ __ bind(&done);
+ }
+ frame_->Push(rax);
+}
+
+
class DeferredSearchCache: public DeferredCode {
public:
DeferredSearchCache(Register dst,
void GenerateRegExpConstructResult(ZoneList<Expression*>* args);
+ void GenerateRegExpCloneResult(ZoneList<Expression*>* args);
+
// Support for fast native caches.
void GenerateGetFromCache(ZoneList<Expression*>* args);
}
+void FullCodeGenerator::EmitRegExpCloneResult(ZoneList<Expression*>* args) {
+ ASSERT(args->length() == 1);
+ VisitForValue(args->at(0), kStack);
+ __ CallRuntime(Runtime::kRegExpCloneResult, 1);
+ Apply(context_, rax);
+}
+
+
void FullCodeGenerator::EmitSwapElements(ZoneList<Expression*>* args) {
ASSERT(args->length() == 3);
VisitForValue(args->at(0), kStack);
assertRegExpTest(/[,b]\B[,b]/, ",b", false);
assertRegExpTest(/[,b]\b[,b]/, "b,", true);
assertRegExpTest(/[,b]\B[,b]/, "b,", false);
+
+// Test that caching of result doesn't share result objects.
+// More iterations increases the chance of hitting a GC.
+for (var i = 0; i < 100; i++) {
+ var re = /x(y)z/;
+ var res = re.exec("axyzb");
+ assertTrue(!!res);
+ assertEquals(2, res.length);
+ assertEquals("xyz", res[0]);
+ assertEquals("y", res[1]);
+ assertEquals(1, res.index);
+ assertEquals("axyzb", res.input);
+ assertEquals(undefined, res.foobar);
+
+ res.foobar = "Arglebargle";
+ res[3] = "Glopglyf";
+ assertEquals("Arglebargle", res.foobar);
+}