| // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| // for details. All rights reserved. Use of this source code is governed by a |
| // BSD-style license that can be found in the LICENSE file. |
| |
| #include "vm/ast.h" |
| #include "vm/compiler.h" |
| #include "vm/dart_entry.h" |
| #include "vm/isolate.h" |
| #include "vm/object_store.h" |
| #include "vm/resolver.h" |
| |
| |
| namespace dart { |
| |
| #define DEFINE_VISIT_FUNCTION(type, name) \ |
| void type::Visit(AstNodeVisitor* visitor) { \ |
| visitor->Visit##type(this); \ |
| } |
| NODE_LIST(DEFINE_VISIT_FUNCTION) |
| #undef DEFINE_VISIT_FUNCTION |
| |
| |
| #define DEFINE_NAME_FUNCTION(type, name) \ |
| const char* type::ShortName() const { \ |
| return name; \ |
| } |
| NODE_LIST(DEFINE_NAME_FUNCTION) |
| #undef DEFINE_NAME_FUNCTION |
| |
| |
| // A visitor class to collect all the nodes (including children) into an |
| // array. |
| class AstNodeCollector : public AstNodeVisitor { |
| public: |
| explicit AstNodeCollector(GrowableArray<AstNode*>* nodes) |
| : nodes_(nodes) { } |
| |
| #define DEFINE_VISITOR_FUNCTION(type, name) \ |
| virtual void Visit##type(type* node) { \ |
| nodes_->Add(node); \ |
| node->VisitChildren(this); \ |
| } |
| NODE_LIST(DEFINE_VISITOR_FUNCTION) |
| #undef DEFINE_VISITOR_FUNCTION |
| |
| private: |
| GrowableArray<AstNode*>* nodes_; |
| DISALLOW_COPY_AND_ASSIGN(AstNodeCollector); |
| }; |
| |
| |
| void SequenceNode::CollectAllNodes(GrowableArray<AstNode*>* nodes) { |
| AstNodeCollector node_collector(nodes); |
| this->Visit(&node_collector); |
| } |
| |
| |
| void SequenceNode::VisitChildren(AstNodeVisitor* visitor) const { |
| for (intptr_t i = 0; i < this->length(); i++) { |
| NodeAt(i)->Visit(visitor); |
| } |
| } |
| |
| |
| void PrimaryNode::VisitChildren(AstNodeVisitor* visitor) const { |
| } |
| |
| |
| void ArgumentListNode::VisitChildren(AstNodeVisitor* visitor) const { |
| for (intptr_t i = 0; i < this->length(); i++) { |
| NodeAt(i)->Visit(visitor); |
| } |
| } |
| |
| |
| void ArrayNode::VisitChildren(AstNodeVisitor* visitor) const { |
| for (intptr_t i = 0; i < this->length(); i++) { |
| ElementAt(i)->Visit(visitor); |
| } |
| } |
| |
| |
| // TODO(srdjan): Add code for logical negation. |
| AstNode* LiteralNode::ApplyUnaryOp(Token::Kind unary_op_kind) { |
| if (unary_op_kind == Token::kNEGATE) { |
| if (literal().IsSmi()) { |
| const Smi& smi = Smi::Cast(literal()); |
| const Instance& literal = |
| Instance::ZoneHandle(Integer::New(-smi.Value())); |
| return new LiteralNode(this->token_pos(), literal); |
| } |
| if (literal().IsDouble()) { |
| const Double& dbl = Double::Cast(literal()); |
| // Preserve negative zero. |
| double new_value = (dbl.value() == 0.0) ? -0.0 : (0.0 - dbl.value()); |
| const Double& double_instance = |
| Double::ZoneHandle(Double::NewCanonical(new_value)); |
| return new LiteralNode(this->token_pos(), double_instance); |
| } |
| } |
| return NULL; |
| } |
| |
| |
| bool ComparisonNode::IsKindValid() const { |
| return Token::IsRelationalOperator(kind_) |
| || Token::IsEqualityOperator(kind_) |
| || Token::IsTypeTestOperator(kind_) |
| || Token::IsTypeCastOperator(kind_); |
| } |
| |
| |
| const char* ComparisonNode::Name() const { |
| return Token::Str(kind_); |
| } |
| |
| |
| const Instance* ComparisonNode::EvalConstExpr() const { |
| const Instance* left_val = this->left()->EvalConstExpr(); |
| if (left_val == NULL) { |
| return NULL; |
| } |
| const Instance* right_val = this->right()->EvalConstExpr(); |
| if (right_val == NULL) { |
| return NULL; |
| } |
| switch (kind_) { |
| case Token::kLT: |
| case Token::kGT: |
| case Token::kLTE: |
| case Token::kGTE: |
| if ((left_val->IsNumber() || left_val->IsNull()) && |
| (right_val->IsNumber() || right_val->IsNull())) { |
| return &Bool::False(); |
| } |
| return NULL; |
| case Token::kEQ: |
| case Token::kNE: |
| case Token::kEQ_STRICT: |
| case Token::kNE_STRICT: |
| // The comparison is a compile time const if both operands are either a |
| // number, string, or boolean value (but not necessarily the same type). |
| if ((left_val->IsNumber() || |
| left_val->IsString() || |
| left_val->IsBool() || |
| left_val->IsNull()) && |
| (right_val->IsNumber() || |
| right_val->IsString() || |
| right_val->IsBool() || |
| right_val->IsNull())) { |
| return &Bool::False(); |
| } |
| return NULL; |
| default: |
| return NULL; |
| } |
| return NULL; |
| } |
| |
| |
| |
| bool BinaryOpNode::IsKindValid() const { |
| switch (kind_) { |
| case Token::kADD: |
| case Token::kSUB: |
| case Token::kMUL: |
| case Token::kDIV: |
| case Token::kTRUNCDIV: |
| case Token::kMOD: |
| case Token::kOR: |
| case Token::kAND: |
| case Token::kBIT_OR: |
| case Token::kBIT_XOR: |
| case Token::kBIT_AND: |
| case Token::kSHL: |
| case Token::kSHR: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| |
| const char* BinaryOpNode::Name() const { |
| return Token::Str(kind_); |
| } |
| |
| |
| const Instance* BinaryOpNode::EvalConstExpr() const { |
| const Instance* left_val = this->left()->EvalConstExpr(); |
| if (left_val == NULL) { |
| return NULL; |
| } |
| if (!left_val->IsNumber() && !left_val->IsBool()) { |
| return NULL; |
| } |
| const Instance* right_val = this->right()->EvalConstExpr(); |
| if (right_val == NULL) { |
| return NULL; |
| } |
| switch (kind_) { |
| case Token::kADD: |
| case Token::kSUB: |
| case Token::kMUL: |
| case Token::kDIV: |
| case Token::kMOD: |
| case Token::kTRUNCDIV: |
| if (left_val->IsInteger()) { |
| if (right_val->IsInteger()) { |
| return left_val; |
| } else if (right_val->IsNumber()) { |
| return right_val; |
| } |
| } else if (left_val->IsNumber() && |
| right_val->IsNumber()) { |
| return left_val; |
| } |
| return NULL; |
| case Token::kBIT_OR: |
| case Token::kBIT_XOR: |
| case Token::kBIT_AND: |
| case Token::kSHL: |
| case Token::kSHR: |
| if (left_val->IsInteger() && |
| right_val->IsInteger()) { |
| return right_val; |
| } |
| return NULL; |
| case Token::kOR: |
| case Token::kAND: |
| if (left_val->IsBool() && right_val->IsBool()) { |
| return left_val; |
| } |
| return NULL; |
| default: |
| UNREACHABLE(); |
| return NULL; |
| } |
| return NULL; |
| } |
| |
| |
| AstNode* UnaryOpNode::UnaryOpOrLiteral(intptr_t token_pos, |
| Token::Kind kind, |
| AstNode* operand) { |
| AstNode* new_operand = operand->ApplyUnaryOp(kind); |
| if (new_operand != NULL) { |
| return new_operand; |
| } |
| return new UnaryOpNode(token_pos, kind, operand); |
| } |
| |
| |
| bool UnaryOpNode::IsKindValid() const { |
| switch (kind_) { |
| case Token::kADD: |
| case Token::kNEGATE: |
| case Token::kNOT: |
| case Token::kBIT_NOT: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| |
| const Instance* UnaryOpNode::EvalConstExpr() const { |
| const Instance* val = this->operand()->EvalConstExpr(); |
| if (val == NULL) { |
| return NULL; |
| } |
| switch (kind_) { |
| case Token::kADD: |
| case Token::kNEGATE: |
| return val->IsNumber() ? val : NULL; |
| case Token::kNOT: |
| return val->IsBool() ? val : NULL; |
| case Token::kBIT_NOT: |
| return val->IsInteger() ? val : NULL; |
| default: |
| return NULL; |
| } |
| } |
| |
| |
| const Instance* ClosureNode::EvalConstExpr() const { |
| if (function().IsImplicitStaticClosureFunction()) { |
| // Return a value that represents an instance. Only the type is relevant. |
| return &Instance::Handle(); |
| } |
| return NULL; |
| } |
| |
| |
| AstNode* ClosureNode::MakeAssignmentNode(AstNode* rhs) { |
| if (scope() == NULL) { |
| // This is an implicit closure node created because a static getter was not |
| // found. Change the getter into a setter. If it does not exist, |
| // noSuchMethod will be called. |
| return new StaticSetterNode(token_pos(), |
| receiver(), |
| Class::ZoneHandle(function().Owner()), |
| String::ZoneHandle(function().name()), |
| rhs); |
| } |
| return NULL; |
| } |
| |
| |
| const char* UnaryOpNode::Name() const { |
| return Token::Str(kind_); |
| } |
| |
| |
| const char* JumpNode::Name() const { |
| return Token::Str(kind_); |
| } |
| |
| |
| const Instance* LoadLocalNode::EvalConstExpr() const { |
| if (local().IsConst()) { |
| return local().ConstValue(); |
| } |
| return NULL; |
| } |
| |
| |
| AstNode* LoadLocalNode::MakeAssignmentNode(AstNode* rhs) { |
| if (local().is_final()) { |
| return NULL; |
| } |
| if (HasPseudo()) { |
| return NULL; |
| } |
| return new StoreLocalNode(token_pos(), &local(), rhs); |
| } |
| |
| |
| AstNode* LoadStaticFieldNode::MakeAssignmentNode(AstNode* rhs) { |
| if (field().is_final()) { |
| return NULL; |
| } |
| return new StoreStaticFieldNode(token_pos(), field(), rhs); |
| } |
| |
| |
| AstNode* InstanceGetterNode::MakeAssignmentNode(AstNode* rhs) { |
| return new InstanceSetterNode(token_pos(), receiver(), field_name(), rhs); |
| } |
| |
| |
| AstNode* LoadIndexedNode::MakeAssignmentNode(AstNode* rhs) { |
| return new StoreIndexedNode(token_pos(), array(), index_expr(), |
| rhs, super_class()); |
| } |
| |
| |
| AstNode* StaticGetterNode::MakeAssignmentNode(AstNode* rhs) { |
| const String& setter_name = String::Handle(Field::SetterName(field_name())); |
| |
| if (is_super_getter_) { |
| ASSERT(receiver() != NULL); |
| // If the static setter is not found in the superclass, noSuchMethod will be |
| // called at runtime. |
| return new StaticSetterNode(token_pos(), |
| receiver(), |
| cls(), |
| field_name(), |
| rhs); |
| } |
| const Function& setter = |
| Function::ZoneHandle(cls().LookupStaticFunction(setter_name)); |
| if (!setter.IsNull()) { |
| return new StaticSetterNode(token_pos(), NULL, cls(), field_name(), rhs); |
| } |
| // Could not find a static setter. Look for a field. |
| // Access to a lazily initialized static field that has not yet been |
| // initialized is compiled to a static implicit getter. |
| // A setter may not exist for such a field. |
| const Field& field = Field::ZoneHandle(cls().LookupStaticField(field_name())); |
| if (!field.IsNull()) { |
| if (field.is_final()) { |
| // Attempting to assign to a final variable will cause a NoSuchMethodError |
| // to be thrown. Change static getter to non-existent static setter in |
| // order to trigger the throw at runtime. |
| return new StaticSetterNode(token_pos(), NULL, cls(), field_name(), rhs); |
| } |
| #if defined(DEBUG) |
| const String& getter_name = String::Handle(Field::GetterName(field_name())); |
| const Function& getter = |
| Function::Handle(cls().LookupStaticFunction(getter_name)); |
| ASSERT(!getter.IsNull() && |
| (getter.kind() == RawFunction::kConstImplicitGetter)); |
| #endif |
| return new StoreStaticFieldNode(token_pos(), field, rhs); |
| } |
| // Didn't find a static setter or a static field. |
| // If this static getter is in an instance function where |
| // a receiver is available, we turn this static getter |
| // into an instance setter (and will get an error at runtime if an |
| // instance setter cannot be found either). |
| if (receiver() != NULL) { |
| return new InstanceSetterNode(token_pos(), receiver(), field_name(), rhs); |
| } |
| return new StaticSetterNode(token_pos(), NULL, cls(), field_name(), rhs); |
| } |
| |
| |
| AstNode* StaticCallNode::MakeAssignmentNode(AstNode* rhs) { |
| // Return this node if it represents a 'throw NoSuchMethodError' indicating |
| // that a getter was not found, otherwise return null. |
| const Class& cls = Class::Handle(function().Owner()); |
| const String& cls_name = String::Handle(cls.Name()); |
| const String& func_name = String::Handle(function().name()); |
| if (cls_name.Equals(Symbols::NoSuchMethodError()) && |
| func_name.StartsWith(Symbols::ThrowNew())) { |
| return this; |
| } |
| return NULL; |
| } |
| |
| |
| const Instance* StaticGetterNode::EvalConstExpr() const { |
| const String& getter_name = |
| String::Handle(Field::GetterName(this->field_name())); |
| const Function& getter_func = |
| Function::Handle(this->cls().LookupStaticFunction(getter_name)); |
| if (getter_func.IsNull() || !getter_func.is_const()) { |
| return NULL; |
| } |
| const Object& result = Object::Handle( |
| DartEntry::InvokeFunction(getter_func, Object::empty_array())); |
| if (result.IsError() || result.IsNull()) { |
| // TODO(turnidge): We could get better error messages by returning |
| // the Error object directly to the parser. This will involve |
| // replumbing all of the EvalConstExpr methods. |
| return NULL; |
| } |
| return &Instance::ZoneHandle(Instance::Cast(result).raw()); |
| } |
| |
| } // namespace dart |