virtual void MarkAsStatement() { /* do nothing */ }
// Static type information for this expression.
- StaticType* type() { return &type_; }
+ SmiAnalysis* type() { return &type_; }
private:
- StaticType type_;
+ SmiAnalysis type_;
};
V(TO_OBJECT, 0) \
V(TO_NUMBER, 0) \
V(TO_STRING, 0) \
+ V(STRING_ADD_LEFT, 1) \
+ V(STRING_ADD_RIGHT, 1) \
V(APPLY_PREPARE, 1) \
V(APPLY_OVERFLOW, 1)
void CodeGenerator::GenericBinaryOperation(Token::Value op,
- StaticType* type,
+ SmiAnalysis* type,
OverwriteMode overwrite_mode) {
Comment cmnt(masm_, "[ BinaryOperation");
Comment cmnt_token(masm_, Token::String(op));
Result right = frame_->Pop();
Result left = frame_->Pop();
+
+ if (op == Token::ADD) {
+ bool left_is_string = left.static_type().is_jsstring();
+ bool right_is_string = right.static_type().is_jsstring();
+ if (left_is_string || right_is_string) {
+ frame_->Push(&left);
+ frame_->Push(&right);
+ Result answer(this);
+ if (left_is_string) {
+ if (right_is_string) {
+ // TODO(lrn): if (left.is_constant() && right.is_constant())
+ // -- do a compile time cons, if allocation during codegen is allowed.
+ answer = frame_->CallRuntime(Runtime::kStringAdd, 2);
+ } else {
+ answer =
+ frame_->InvokeBuiltin(Builtins::STRING_ADD_LEFT, CALL_FUNCTION, 2);
+ }
+ } else if (right_is_string) {
+ answer =
+ frame_->InvokeBuiltin(Builtins::STRING_ADD_RIGHT, CALL_FUNCTION, 2);
+ }
+ answer.set_static_type(StaticType::jsstring());
+ frame_->Push(&answer);
+ return;
+ }
+ // Neither operand is known to be a string.
+ }
+
bool left_is_smi = left.is_constant() && left.handle()->IsSmi();
bool left_is_non_smi = left.is_constant() && !left.handle()->IsSmi();
bool right_is_smi = right.is_constant() && right.handle()->IsSmi();
void CodeGenerator::ConstantSmiBinaryOperation(Token::Value op,
Result* operand,
Handle<Object> value,
- StaticType* type,
+ SmiAnalysis* type,
bool reversed,
OverwriteMode overwrite_mode) {
// NOTE: This is an attempt to inline (a bit) more of the code for
void CodeGenerator::VisitLiteral(Literal* node) {
Comment cmnt(masm_, "[ Literal");
- frame_->Push(node->handle());
- }
+ frame_->Push(node->handle());
+}
void CodeGenerator::LoadUnsafeSmi(Register target, Handle<Object> value) {
// control destination.
void ToBoolean(ControlDestination* destination);
- void GenericBinaryOperation(Token::Value op,
- StaticType* type,
+ void GenericBinaryOperation(
+ Token::Value op,
+ SmiAnalysis* type,
const OverwriteMode overwrite_mode = NO_OVERWRITE);
// If possible, combine two constant smi values using op to produce
void ConstantSmiBinaryOperation(Token::Value op,
Result* operand,
Handle<Object> constant_operand,
- StaticType* type,
+ SmiAnalysis* type,
bool reversed,
OverwriteMode overwrite_mode);
void JumpTarget::Unuse() {
// We should not deallocate jump targets that have unresolved jumps
// to them. In the event of a compile-time stack overflow or an
- // unitialized jump target, we don't care.
+ // uninitialized jump target, we don't care.
ASSERT(!is_linked() || cgen_ == NULL || cgen_->HasStackOverflow());
for (int i = 0; i < reaching_frames_.length(); i++) {
delete reaching_frames_[i];
// If they have the same value, the result is the same. If either
// is unsynced, the result is.
+
if (left->is_memory() && right->is_memory()) return left;
if (left->is_register() && right->is_register() &&
for (int i = 0; i < length; i++) {
FrameElement element = initial_frame->elements_[i];
// We do not allow copies or constants in bidirectional frames.
- if (direction_ == BIDIRECTIONAL &&
- i > high_water_mark &&
+ if (direction_ == BIDIRECTIONAL && i > high_water_mark &&
(element.is_constant() || element.is_copy())) {
elements.Add(NULL);
} else {
// the backing store of copies is always lower in the frame.
// Set the register locations to their index in the frame.
for (int i = 0; i < length; i++) {
- FrameElement current = entry_frame_->elements_[i];
- entry_frame_->elements_[i].clear_copied();
- if (current.is_copy()) {
- entry_frame_->elements_[current.index()].set_copied();
- } else if (current.is_register()) {
- entry_frame_->register_locations_[current.reg().code()] = i;
+ FrameElement* current = &entry_frame_->elements_[i];
+ current->clear_copied();
+ if (current->is_copy()) {
+ entry_frame_->elements_[current->index()].set_copied();
+ } else if (current->is_register()) {
+ entry_frame_->register_locations_[current->reg().code()] = i;
+ }
+
+ if (direction_ == BIDIRECTIONAL && i >= high_water_mark) {
+ current->set_static_type(StaticType::unknown());
+ } else {
+ StaticType merged_type = reaching_frames_[0]->elements_[i].static_type();
+ for (int j = 1, n = reaching_frames_.length();
+ !merged_type.is_unknown() && j < n;
+ j++) {
+ merged_type =
+ merged_type.merge(reaching_frames_[j]->elements_[i].static_type());
+ }
+ current->set_static_type(merged_type);
}
}
ast_printer_->inc_indent();
}
- explicit IndentedScope(const char* txt, StaticType* type = NULL) {
+ explicit IndentedScope(const char* txt, SmiAnalysis* type = NULL) {
ast_printer_->PrintIndented(txt);
if ((type != NULL) && (type->IsKnown())) {
ast_printer_->Print(" (type = ");
- ast_printer_->Print(StaticType::Type2String(type));
+ ast_printer_->Print(SmiAnalysis::Type2String(type));
ast_printer_->Print(")");
}
ast_printer_->Print("\n");
void AstPrinter::PrintLiteralWithModeIndented(const char* info,
Variable* var,
Handle<Object> value,
- StaticType* type) {
+ SmiAnalysis* type) {
if (var == NULL) {
PrintLiteralIndented(info, value, true);
} else {
if (type->IsKnown()) {
OS::SNPrintF(buf, "%s (mode = %s, type = %s)", info,
Variable::Mode2String(var->mode()),
- StaticType::Type2String(type));
+ SmiAnalysis::Type2String(type));
} else {
OS::SNPrintF(buf, "%s (mode = %s)", info,
Variable::Mode2String(var->mode()));
OS::SNPrintF(buf, "%s %s (type = %s)",
(node->is_prefix() ? "PRE" : "POST"),
Token::Name(node->op()),
- StaticType::Type2String(node->type()));
+ SmiAnalysis::Type2String(node->type()));
} else {
OS::SNPrintF(buf, "%s %s", (node->is_prefix() ? "PRE" : "POST"),
Token::Name(node->op()));
void PrintLiteralWithModeIndented(const char* info,
Variable* var,
Handle<Object> value,
- StaticType* type);
+ SmiAnalysis* type);
void PrintLabelsIndented(const char* info, ZoneStringList* labels);
void inc_indent() { indent_++; }
#ifndef V8_REGISTER_ALLOCATOR_INL_H_
#define V8_REGISTER_ALLOCATOR_INL_H_
+#include "register-allocator.h"
#include "virtual-frame.h"
namespace v8 { namespace internal {
// Result implementation.
Result::Result(Register reg, CodeGenerator* cgen)
- : type_(REGISTER),
- cgen_(cgen) {
+ : static_type_(),
+ type_(REGISTER),
+ cgen_(cgen) {
+ data_.reg_ = reg;
+ ASSERT(reg.is_valid());
+ cgen_->allocator()->Use(reg);
+}
+
+
+Result::Result(Register reg, CodeGenerator* cgen, StaticType static_type)
+ : static_type_(static_type),
+ type_(REGISTER),
+ cgen_(cgen) {
data_.reg_ = reg;
ASSERT(reg.is_valid());
cgen_->allocator()->Use(reg);
void Result::CopyTo(Result* destination) const {
+ destination->static_type_ = static_type_;
destination->type_ = type();
destination->cgen_ = cgen_;
namespace v8 { namespace internal {
+
+// -------------------------------------------------------------------------
+// StaticType
+//
+// StaticType represent the type of an expression or a word at runtime.
+// The types are ordered by knowledge, so that if a value can come about
+// in more than one way, and there are different static types inferred
+// for the different ways, the types can be combined to a type that we
+// are still certain of (possibly just "unknown").
+
+class StaticType BASE_EMBEDDED {
+ public:
+ StaticType() : static_type_(UNKNOWN_TYPE) {}
+
+ static StaticType unknown() { return StaticType(); }
+ static StaticType smi() { return StaticType(SMI_TYPE); }
+ static StaticType jsstring() { return StaticType(STRING_TYPE); }
+ static StaticType heap_object() { return StaticType(HEAP_OBJECT_TYPE); }
+
+ // Accessors
+ bool is_unknown() { return static_type_ == UNKNOWN_TYPE; }
+ bool is_smi() { return static_type_ == SMI_TYPE; }
+ bool is_heap_object() { return (static_type_ & HEAP_OBJECT_TYPE) != 0; }
+ bool is_jsstring() { return static_type_ == STRING_TYPE; }
+
+ bool operator==(StaticType other) const {
+ return static_type_ == other.static_type_;
+ }
+
+ // Find the best approximating type for a value.
+ // The argument must not be NULL.
+ static StaticType TypeOf(Object* object) {
+ // Remember to make the most specific tests first. A string is also a heap
+ // object, so test for string-ness first.
+ if (object->IsSmi()) return smi();
+ if (object->IsString()) return jsstring();
+ if (object->IsHeapObject()) return heap_object();
+ return unknown();
+ }
+
+ // Merges two static types to a type that combines the knowledge
+ // of both. If there is no way to combine (e.g., being a string *and*
+ // being a smi), the resulting type is unknown.
+ StaticType merge(StaticType other) {
+ StaticType x(
+ static_cast<StaticTypeEnum>(static_type_ & other.static_type_));
+ return x;
+ }
+
+ private:
+ enum StaticTypeEnum {
+ // Numbers are chosen so that least upper bound of the following
+ // partial order is implemented by bitwise "and":
+ //
+ // string
+ // |
+ // heap-object smi
+ // \ /
+ // unknown
+ //
+ UNKNOWN_TYPE = 0x00,
+ SMI_TYPE = 0x01,
+ HEAP_OBJECT_TYPE = 0x02,
+ STRING_TYPE = 0x04 | HEAP_OBJECT_TYPE
+ };
+ explicit StaticType(StaticTypeEnum static_type) : static_type_(static_type) {}
+
+ // StaticTypeEnum static_type_;
+ byte static_type_;
+};
+
+
// -------------------------------------------------------------------------
// Results
//
};
// Construct an invalid result.
- explicit Result(CodeGenerator* cgen) : type_(INVALID), cgen_(cgen) {}
+ explicit Result(CodeGenerator* cgen)
+ : static_type_(),
+ type_(INVALID),
+ cgen_(cgen) {}
// Construct a register Result.
- Result(Register reg, CodeGenerator* cgen);
+ Result(Register reg,
+ CodeGenerator* cgen);
+
+ // Construct a register Result with a known static type.
+ Result(Register reg,
+ CodeGenerator* cgen,
+ StaticType static_type);
// Construct a Result whose value is a compile-time constant.
Result(Handle<Object> value, CodeGenerator * cgen)
- : type_(CONSTANT),
+ : static_type_(StaticType::TypeOf(*value)),
+ type_(CONSTANT),
cgen_(cgen) {
data_.handle_ = value.location();
}
inline void Unuse();
- Type type() const { return type_; }
+ StaticType static_type() const { return static_type_; }
+ void set_static_type(StaticType static_type) { static_type_ = static_type; }
+
+ Type type() const { return static_cast<Type>(type_); }
bool is_valid() const { return type() != INVALID; }
bool is_register() const { return type() == REGISTER; }
void ToRegister(Register reg);
private:
- Type type_;
+ StaticType static_type_;
+ byte type_;
union {
Register reg_;
if (proxy != NULL) {
Variable* var = proxy->AsVariable();
if (var != NULL) {
- StaticType* var_type = var->type();
+ SmiAnalysis* var_type = var->type();
if (var_type->IsUnknown()) {
var_type->CopyFrom(node->type());
} else if (var_type->IsLikelySmi()) {
}
+// Left operand (this) is already a string.
+function STRING_ADD_LEFT(x) {
+ x = %ToString(%ToPrimitive(x, NO_HINT));
+ return %StringAdd(this, x);
+}
+
+
+// Right operand (x) is already a string.
+function STRING_ADD_RIGHT(x) {
+ var a = %ToString(%ToPrimitive(this, NO_HINT));
+ return %StringAdd(a, x);
+}
+
+
// ECMA-262, section 11.6.2, page 50.
function SUB(x) {
return %NumberSub(%ToNumber(this), %ToNumber(x));
// ECMA-262, section 11.8.6, page 54. To make the implementation more
-// efficient, the return value should be zero if the 'this' is an
+// efficient, the return value should be zero if the 'this' is an
// instance of F, and non-zero if not. This makes it possible to avoid
// an expensive ToBoolean conversion in the generated code.
function INSTANCE_OF(F) {
// ----------------------------------------------------------------------------
-// Implementation StaticType.
+// Implementation SmiAnalysis.
-const char* StaticType::Type2String(StaticType* type) {
+const char* SmiAnalysis::Type2String(SmiAnalysis* type) {
switch (type->kind_) {
case UNKNOWN:
return "UNKNOWN";
// Variables and AST expression nodes can track their "type" to enable
// optimizations and removal of redundant checks when generating code.
-class StaticType BASE_EMBEDDED {
+class SmiAnalysis {
public:
enum Kind {
UNKNOWN,
LIKELY_SMI
};
- StaticType() : kind_(UNKNOWN) {}
+ SmiAnalysis() : kind_(UNKNOWN) {}
bool Is(Kind kind) const { return kind_ == kind; }
bool IsUnknown() const { return Is(UNKNOWN); }
bool IsLikelySmi() const { return Is(LIKELY_SMI); }
- void CopyFrom(StaticType* other) {
+ void CopyFrom(SmiAnalysis* other) {
kind_ = other->kind_;
}
- static const char* Type2String(StaticType* type);
+ static const char* Type2String(SmiAnalysis* type);
// LIKELY_SMI accessors
void SetAsLikelySmi() {
private:
Kind kind_;
- DISALLOW_COPY_AND_ASSIGN(StaticType);
+ DISALLOW_COPY_AND_ASSIGN(SmiAnalysis);
};
Expression* rewrite() const { return rewrite_; }
Slot* slot() const;
- StaticType* type() { return &type_; }
+ SmiAnalysis* type() { return &type_; }
private:
Variable(Scope* scope, Handle<String> name, Mode mode, bool is_valid_LHS,
UseCount obj_uses_; // uses of the object the variable points to
// Static type information
- StaticType type_;
+ SmiAnalysis type_;
// Code generation.
// rewrite_ is usually a Slot or a Property, but maybe any expression.
void EmitPush(Register reg);
// Push an element on the virtual frame.
- void Push(Register reg);
+ void Push(Register reg, StaticType static_type = StaticType());
void Push(Handle<Object> value);
void Push(Smi* value) { Push(Handle<Object>(value)); }
- // Pushing a result invalidates it (its contents become owned by the
- // frame).
+ // Pushing a result invalidates it (its contents become owned by the frame).
void Push(Result* result);
// Nip removes zero or more elements from immediately below the top
if (elements_[i].is_copy()) {
elements_[elements_[i].index()].set_copied();
}
+ elements_[i].set_static_type(target.static_type());
}
- // Adjust the stack point downard if necessary.
+ // Adjust the stack pointer downward if necessary.
if (stack_pointer_ > expected->stack_pointer_) {
int difference = stack_pointer_ - expected->stack_pointer_;
stack_pointer_ = expected->stack_pointer_;
if (element.is_memory()) {
Result temp = cgen_->allocator()->Allocate();
ASSERT(temp.is_valid());
+ temp.set_static_type(element.static_type());
__ pop(temp.reg());
return temp;
}
FrameElement::RegisterElement(temp.reg(), FrameElement::SYNCED);
// Preserve the copy flag on the element.
if (element.is_copied()) new_element.set_copied();
+ new_element.set_static_type(element.static_type());
elements_[index] = new_element;
__ mov(temp.reg(), Operand(ebp, fp_relative(index)));
- return Result(temp.reg(), cgen_);
+ return Result(temp.reg(), cgen_, element.static_type());
} else if (element.is_register()) {
- return Result(element.reg(), cgen_);
+ return Result(element.reg(), cgen_, element.static_type());
} else {
ASSERT(element.is_constant());
return Result(element.handle(), cgen_);
void EmitPush(Immediate immediate);
// Push an element on the virtual frame.
- void Push(Register reg);
+ void Push(Register reg, StaticType static_type = StaticType());
void Push(Handle<Object> value);
void Push(Smi* value) { Push(Handle<Object>(value)); }
case FrameElement::MEMORY: // Fall through.
case FrameElement::REGISTER:
// All copies are backed by memory or register locations.
- result.type_ =
- FrameElement::TypeField::encode(FrameElement::COPY)
- | FrameElement::IsCopiedField::encode(false)
- | FrameElement::SyncField::encode(FrameElement::NOT_SYNCED);
+ result.set_static_type(target.static_type());
+ result.type_ = FrameElement::COPY;
+ result.copied_ = false;
+ result.synced_ = false;
result.data_.index_ = index;
elements_[index].set_copied();
break;
if (elements_[index].is_register()) {
Unuse(elements_[index].reg());
}
+ new_element.set_static_type(elements_[index].static_type());
elements_[index] = new_element;
}
// register element, or the new element at frame_index, must be made
// a copy.
int i = register_index(value->reg());
+ ASSERT(value->static_type() == elements_[i].static_type());
+
if (i < frame_index) {
// The register FrameElement is lower in the frame than the new copy.
elements_[frame_index] = CopyElementAt(i);
Use(value->reg(), frame_index);
elements_[frame_index] =
FrameElement::RegisterElement(value->reg(),
- FrameElement::NOT_SYNCED);
+ FrameElement::NOT_SYNCED,
+ value->static_type());
}
} else {
ASSERT(value->is_constant());
}
-void VirtualFrame::Push(Register reg) {
+void VirtualFrame::Push(Register reg, StaticType static_type) {
if (is_used(reg)) {
- elements_.Add(CopyElementAt(register_index(reg)));
+ int index = register_index(reg);
+ FrameElement element = CopyElementAt(index);
+ ASSERT(static_type.merge(element.static_type()) == element.static_type());
+ elements_.Add(element);
} else {
Use(reg, elements_.length());
- elements_.Add(FrameElement::RegisterElement(reg, FrameElement::NOT_SYNCED));
+ FrameElement element =
+ FrameElement::RegisterElement(reg,
+ FrameElement::NOT_SYNCED,
+ static_type);
+ elements_.Add(element);
}
}
void VirtualFrame::Push(Handle<Object> value) {
- elements_.Add(FrameElement::ConstantElement(value,
- FrameElement::NOT_SYNCED));
+ FrameElement element =
+ FrameElement::ConstantElement(value, FrameElement::NOT_SYNCED);
+ elements_.Add(element);
}
void VirtualFrame::Push(Result* result) {
if (result->is_register()) {
- Push(result->reg());
+ Push(result->reg(), result->static_type());
} else {
ASSERT(result->is_constant());
Push(result->handle());
bool FrameElement::Equals(FrameElement other) {
- if (type_ != other.type_) return false;
+ if (type_ != other.type_ ||
+ copied_ != other.copied_ ||
+ synced_ != other.synced_) return false;
if (is_register()) {
if (!reg().is(other.reg())) return false;
class FrameElement BASE_EMBEDDED {
public:
enum SyncFlag {
- SYNCED,
- NOT_SYNCED
+ NOT_SYNCED,
+ SYNCED
};
// The default constructor creates an invalid frame element.
- FrameElement() {
- Initialize(INVALID, no_reg, NOT_SYNCED);
+ FrameElement()
+ : static_type_(), type_(INVALID), copied_(false), synced_(false) {
+ data_.reg_ = no_reg;
}
// Factory function to construct an invalid frame element.
}
// Factory function to construct an in-register frame element.
- static FrameElement RegisterElement(Register reg, SyncFlag is_synced) {
- FrameElement result(REGISTER, reg, is_synced);
- return result;
+ static FrameElement RegisterElement(Register reg,
+ SyncFlag is_synced,
+ StaticType static_type = StaticType()) {
+ return FrameElement(REGISTER, reg, is_synced, static_type);
}
// Factory function to construct a frame element whose value is known at
return result;
}
- bool is_synced() const { return SyncField::decode(type_) == SYNCED; }
+ bool is_synced() const { return synced_; }
void set_sync() {
ASSERT(type() != MEMORY);
- type_ = (type_ & ~SyncField::mask()) | SyncField::encode(SYNCED);
+ synced_ = true;
}
void clear_sync() {
ASSERT(type() != MEMORY);
- type_ = (type_ & ~SyncField::mask()) | SyncField::encode(NOT_SYNCED);
+ synced_ = false;
}
bool is_valid() const { return type() != INVALID; }
bool is_constant() const { return type() == CONSTANT; }
bool is_copy() const { return type() == COPY; }
- bool is_copied() const { return IsCopiedField::decode(type_); }
-
- void set_copied() {
- type_ = (type_ & ~IsCopiedField::mask()) | IsCopiedField::encode(true);
- }
-
- void clear_copied() {
- type_ = (type_ & ~IsCopiedField::mask()) | IsCopiedField::encode(false);
- }
+ bool is_copied() const { return copied_; }
+ void set_copied() { copied_ = true; }
+ void clear_copied() { copied_ = false; }
Register reg() const {
ASSERT(is_register());
bool Equals(FrameElement other);
+ StaticType static_type() { return static_type_; }
+
+ void set_static_type(StaticType static_type) {
+ // TODO(lrn): If it's s copy, it would be better to update the real one,
+ // but we can't from here. The caller must handle this.
+ static_type_ = static_type;
+ }
+
private:
enum Type {
INVALID,
COPY
};
- // BitField is <type, shift, size>.
- class SyncField : public BitField<SyncFlag, 0, 1> {};
- class IsCopiedField : public BitField<bool, 1, 1> {};
- class TypeField : public BitField<Type, 2, 32 - 2> {};
+ Type type() const { return static_cast<Type>(type_); }
+
+ StaticType static_type_;
- Type type() const { return TypeField::decode(type_); }
+ // The element's type.
+ byte type_;
- // The element's type and a dirty bit. The dirty bit can be cleared
+ bool copied_;
+
+ // The element's dirty-bit. The dirty bit can be cleared
// for non-memory elements to indicate that the element agrees with
// the value in memory in the actual frame.
- int type_;
+ bool synced_;
union {
Register reg_;
} data_;
// Used to construct memory and register elements.
- FrameElement(Type type, Register reg, SyncFlag is_synced) {
- Initialize(type, reg, is_synced);
+ FrameElement(Type type, Register reg, SyncFlag is_synced)
+ : static_type_(),
+ type_(type),
+ copied_(false),
+ synced_(is_synced != NOT_SYNCED) {
+ data_.reg_ = reg;
}
- // Used to construct constant elements.
- inline FrameElement(Handle<Object> value, SyncFlag is_synced);
+ FrameElement(Type type, Register reg, SyncFlag is_synced, StaticType stype)
+ : static_type_(stype),
+ type_(type),
+ copied_(false),
+ synced_(is_synced != NOT_SYNCED) {
+ data_.reg_ = reg;
+ }
- // Used to initialize invalid, memory, and register elements.
- inline void Initialize(Type type, Register reg, SyncFlag is_synced);
+ // Used to construct constant elements.
+ FrameElement(Handle<Object> value, SyncFlag is_synced)
+ : static_type_(StaticType::TypeOf(*value)),
+ type_(CONSTANT),
+ copied_(false),
+ synced_(is_synced != NOT_SYNCED) {
+ data_.handle_ = value.location();
+ }
void set_index(int new_index) {
ASSERT(is_copy());
#include "virtual-frame-ia32.h"
#endif
-
-namespace v8 { namespace internal {
-
-FrameElement::FrameElement(Handle<Object> value, SyncFlag is_synced) {
- type_ = TypeField::encode(CONSTANT)
- | IsCopiedField::encode(false)
- | SyncField::encode(is_synced);
- data_.handle_ = value.location();
-}
-
-
-void FrameElement::Initialize(Type type, Register reg, SyncFlag is_synced) {
- type_ = TypeField::encode(type)
- | IsCopiedField::encode(false)
- | SyncField::encode(is_synced);
- data_.reg_ = reg;
-}
-
-
-} } // namespace v8::internal
-
#endif // V8_VIRTUAL_FRAME_H_
--- /dev/null
+// Copyright 2009 the V8 project authors. All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are
+// met:
+//
+// * Redistributions of source code must retain the above copyright
+// notice, this list of conditions and the following disclaimer.
+// * Redistributions in binary form must reproduce the above
+// copyright notice, this list of conditions and the following
+// disclaimer in the documentation and/or other materials provided
+// with the distribution.
+// * Neither the name of Google Inc. nor the names of its
+// contributors may be used to endorse or promote products derived
+// from this software without specific prior written permission.
+//
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+assertEquals("ab", "a" + "b", "ll");
+
+assertEquals("12", "1" + "2", "dd");
+assertEquals("123", "1" + "2" + "3", "ddd");
+assertEquals("123", 1 + "2" + "3", "ndd");
+assertEquals("123", "1" + 2 + "3", "dnd");
+assertEquals("123", "1" + "2" + 3, "ddn");
+
+assertEquals("123", "1" + 2 + 3, "dnn");
+assertEquals("123", 1 + "2" + 3, "ndn");
+assertEquals("33", 1 + 2 + "3", "nnd");
+
+var x = "1";
+assertEquals("12", x + 2, "vn");
+assertEquals("12", x + "2", "vd");
+assertEquals("21", 2 + x, "nv");
+assertEquals("21", "2" + x, "dv");
+
+var y = "2";
+assertEquals("12", x + y, "vdvd");
+
+x = 1;
+assertEquals("12", x + y, "vnvd");
+
+y = 2;
+assertEquals(3, x + y, "vnvn");
+
+x = "1";
+assertEquals("12", x + y, "vdvn");
+
+y = "2";
+assertEquals("12", x + y, "vdvd2");
+
+(function(x, y) {
+ var z = "3";
+ var w = "4";
+
+ assertEquals("11", x + x, "xx");
+ assertEquals("12", x + y, "xy");
+ assertEquals("13", x + z, "xz");
+ assertEquals("14", x + w, "xw");
+
+ assertEquals("21", y + x, "yx");
+ assertEquals("22", y + y, "yy");
+ assertEquals("23", y + z, "yz");
+ assertEquals("24", y + w, "yw");
+
+ assertEquals("31", z + x, "zx");
+ assertEquals("32", z + y, "zy");
+ assertEquals("33", z + z, "zz");
+ assertEquals("34", z + w, "zw");
+
+ assertEquals("41", w + x, "wx");
+ assertEquals("42", w + y, "wy");
+ assertEquals("43", w + z, "wz");
+ assertEquals("44", w + w, "ww");
+
+ (function(){x = 1; z = 3;})();
+
+ assertEquals(2, x + x, "x'x");
+ assertEquals("12", x + y, "x'y");
+ assertEquals(4, x + z, "x'z'");
+ assertEquals("14", x + w, "x'w");
+
+ assertEquals("21", y + x, "yx'");
+ assertEquals("22", y + y, "yy");
+ assertEquals("23", y + z, "yz'");
+ assertEquals("24", y + w, "yw");
+
+ assertEquals(4, z + x, "z'x'");
+ assertEquals("32", z + y, "z'y");
+ assertEquals(6, z + z, "z'z'");
+ assertEquals("34", z + w, "z'w");
+
+ assertEquals("41", w + x, "wx'");
+ assertEquals("42", w + y, "wy");
+ assertEquals("43", w + z, "wz'");
+ assertEquals("44", w + w, "ww");
+})("1", "2");
+
+assertEquals("142", "1" + new Number(42), "sN");
+assertEquals("421", new Number(42) + "1", "Ns");
+assertEquals(84, new Number(42) + new Number(42), "NN");
+
+assertEquals("142", "1" + new String("42"), "sS");
+assertEquals("421", new String("42") + "1", "Ss");
+assertEquals("142", "1" + new String("42"), "sS");
+assertEquals("4242", new String("42") + new String("42"), "SS");
+
+assertEquals("1true", "1" + true, "sb");
+assertEquals("true1", true + "1", "bs");
+assertEquals(2, true + true, "bs");
+
+assertEquals("1true", "1" + new Boolean(true), "sB");
+assertEquals("true1", new Boolean(true) + "1", "Bs");
+assertEquals(2, new Boolean(true) + new Boolean(true), "Bs");
+
+assertEquals("1undefined", "1" + void 0, "sv");
+assertEquals("undefined1", (void 0) + "1", "vs");
+assertTrue(isNaN(void 0 + void 0), "vv");
+
+assertEquals("1null", "1" + null, "su");
+assertEquals("null1", null + "1", "us");
+assertEquals(0, null + null, "uu");
+
+(function (i) {
+ // Check that incoming frames are merged correctly.
+ var x;
+ var y;
+ var z;
+ var w;
+ switch (i) {
+ case 1: x = 42; y = "stry"; z = "strz"; w = 42; break;
+ default: x = "strx", y = 42; z = "strz"; w = 42; break;
+ }
+ var resxx = x + x;
+ var resxy = x + y;
+ var resxz = x + z;
+ var resxw = x + w;
+ var resyx = y + x;
+ var resyy = y + y;
+ var resyz = y + z;
+ var resyw = y + w;
+ var reszx = z + x;
+ var reszy = z + y;
+ var reszz = z + z;
+ var reszw = z + w;
+ var reswx = w + x;
+ var reswy = w + y;
+ var reswz = w + z;
+ var resww = w + w;
+ assertEquals(84, resxx, "swxx");
+ assertEquals("42stry", resxy, "swxy");
+ assertEquals("42strz", resxz, "swxz");
+ assertEquals(84, resxw, "swxw");
+ assertEquals("stry42", resyx, "swyx");
+ assertEquals("strystry", resyy, "swyy");
+ assertEquals("strystrz", resyz, "swyz");
+ assertEquals("stry42", resyw, "swyw");
+ assertEquals("strz42", reszx, "swzx");
+ assertEquals("strzstry", reszy, "swzy");
+ assertEquals("strzstrz", reszz, "swzz");
+ assertEquals("strz42", reszw, "swzw");
+ assertEquals(84, reswx, "swwx");
+ assertEquals("42stry", reswy, "swwy");
+ assertEquals("42strz", reswz, "swwz");
+ assertEquals(84, resww, "swww");
+})(1);