// 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. #if !defined(DART_PRECOMPILED_RUNTIME) #include "vm/ast.h" #include "vm/compiler/jit/compiler.h" #include "vm/dart_entry.h" #include "vm/isolate.h" #include "vm/log.h" #include "vm/object_store.h" #include "vm/resolver.h" namespace dart { #define DEFINE_VISIT_FUNCTION(BaseName) \ void BaseName##Node::Visit(AstNodeVisitor* visitor) { \ visitor->Visit##BaseName##Node(this); \ } FOR_EACH_NODE(DEFINE_VISIT_FUNCTION) #undef DEFINE_VISIT_FUNCTION #define DEFINE_NAME_FUNCTION(BaseName) \ const char* BaseName##Node::Name() const { return #BaseName; } FOR_EACH_NODE(DEFINE_NAME_FUNCTION) #undef DEFINE_NAME_FUNCTION const Field* AstNode::MayCloneField(const Field& value) { if (Compiler::IsBackgroundCompilation() || FLAG_force_clone_compiler_objects) { return &Field::ZoneHandle(value.CloneFromOriginal()); } else { ASSERT(value.IsZoneHandle()); return &value; } } // A visitor class to collect all the nodes (including children) into an // array. class AstNodeCollector : public AstNodeVisitor { public: explicit AstNodeCollector(GrowableArray* nodes) : nodes_(nodes) {} #define DEFINE_VISITOR_FUNCTION(BaseName) \ virtual void Visit##BaseName##Node(BaseName##Node* node) { \ nodes_->Add(node); \ node->VisitChildren(this); \ } FOR_EACH_NODE(DEFINE_VISITOR_FUNCTION) #undef DEFINE_VISITOR_FUNCTION private: GrowableArray* nodes_; DISALLOW_COPY_AND_ASSIGN(AstNodeCollector); }; void SequenceNode::CollectAllNodes(GrowableArray* 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 SequenceNode::Add(AstNode* node) { if (node->IsReturnNode()) { node->AsReturnNode()->set_scope(scope()); } nodes_.Add(node); } 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); } } LetNode::LetNode(TokenPosition token_pos) : AstNode(token_pos), vars_(1), initializers_(1), nodes_(1) {} LocalVariable* LetNode::AddInitializer(AstNode* node) { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); initializers_.Add(node); char name[64]; Utils::SNPrint(name, sizeof(name), ":lt%s_%" Pd "", token_pos().ToCString(), vars_.length()); LocalVariable* temp_var = new LocalVariable(TokenPosition::kNoSource, token_pos(), String::ZoneHandle(zone, Symbols::New(thread, name)), Object::dynamic_type()); vars_.Add(temp_var); return temp_var; } void LetNode::VisitChildren(AstNodeVisitor* visitor) const { for (intptr_t i = 0; i < num_temps(); ++i) { initializers_[i]->Visit(visitor); } for (intptr_t i = 0; i < nodes_.length(); ++i) { nodes_[i]->Visit(visitor); } } bool LetNode::IsPotentiallyConst() const { for (intptr_t i = 0; i < num_temps(); i++) { if (!initializers_[i]->IsPotentiallyConst()) { return false; } } for (intptr_t i = 0; i < nodes_.length(); i++) { if (!nodes_[i]->IsPotentiallyConst()) { return false; } } return true; } const Instance* LetNode::EvalConstExpr() const { for (intptr_t i = 0; i < num_temps(); i++) { if (initializers_[i]->EvalConstExpr() == NULL) { return NULL; } } const Instance* last = NULL; for (intptr_t i = 0; i < nodes_.length(); i++) { last = nodes_[i]->EvalConstExpr(); if (last == NULL) { return NULL; } } return last; } void ArrayNode::VisitChildren(AstNodeVisitor* visitor) const { for (intptr_t i = 0; i < this->length(); i++) { ElementAt(i)->Visit(visitor); } } bool StringInterpolateNode::IsPotentiallyConst() const { for (int i = 0; i < value_->length(); i++) { if (!value_->ElementAt(i)->IsPotentiallyConst()) { return false; } } return true; } bool LiteralNode::IsPotentiallyConst() const { return true; } 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(), Heap::kOld)); return new LiteralNode(this->token_pos(), literal); } if (literal().IsMint()) { const Mint& mint = Mint::Cast(literal()); const Instance& literal = Instance::ZoneHandle(Integer::New(-mint.value(), Heap::kOld)); 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); } } else if (unary_op_kind == Token::kBIT_NOT) { if (literal().IsSmi()) { const Smi& smi = Smi::Cast(literal()); const Instance& literal = Instance::ZoneHandle(Integer::New(~smi.Value(), Heap::kOld)); return new LiteralNode(this->token_pos(), literal); } if (literal().IsMint()) { const Mint& mint = Mint::Cast(literal()); const Instance& literal = Instance::ZoneHandle(Integer::New(~mint.value(), Heap::kOld)); return new LiteralNode(this->token_pos(), literal); } } else if (unary_op_kind == Token::kNOT) { if (literal().IsBool()) { const Bool& boolean = Bool::Cast(literal()); return new LiteralNode(this->token_pos(), Bool::Get(!boolean.value())); } } return NULL; } const char* TypeNode::TypeName() const { return String::Handle(type().UserVisibleName()).ToCString(); } bool ComparisonNode::IsKindValid() const { return Token::IsRelationalOperator(kind_) || Token::IsEqualityOperator(kind_) || Token::IsTypeTestOperator(kind_) || Token::IsTypeCastOperator(kind_); } const char* ComparisonNode::TokenName() const { return (kind_ == Token::kAS) ? "as" : Token::Str(kind_); } bool ComparisonNode::IsPotentiallyConst() const { switch (kind_) { case Token::kLT: case Token::kGT: case Token::kLTE: case Token::kGTE: case Token::kEQ: case Token::kNE: case Token::kEQ_STRICT: case Token::kNE_STRICT: return this->left()->IsPotentiallyConst() && this->right()->IsPotentiallyConst(); default: return false; } } 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: // 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; case Token::kEQ_STRICT: case Token::kNE_STRICT: // identical(a, b) is a compile time const if both operands are // compile time constants, regardless of their type. return &Bool::True(); 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::kIFNULL: 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::TokenName() const { return Token::Str(kind_); } bool BinaryOpNode::IsPotentiallyConst() const { switch (kind_) { case Token::kOR: case Token::kAND: if (this->left()->IsLiteralNode() && this->left()->AsLiteralNode()->literal().IsNull()) { return false; } if (this->right()->IsLiteralNode() && this->right()->AsLiteralNode()->literal().IsNull()) { return false; } /* Falls through */ case Token::kADD: case Token::kSUB: case Token::kMUL: case Token::kDIV: case Token::kMOD: case Token::kTRUNCDIV: case Token::kBIT_OR: case Token::kBIT_XOR: case Token::kBIT_AND: case Token::kSHL: case Token::kSHR: case Token::kIFNULL: return this->left()->IsPotentiallyConst() && this->right()->IsPotentiallyConst(); default: UNREACHABLE(); return false; } } 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() && !left_val->IsString() && kind_ != Token::kIFNULL) { return NULL; } const Instance* right_val = this->right()->EvalConstExpr(); if (right_val == NULL) { return NULL; } switch (kind_) { case Token::kADD: if (left_val->IsString()) { return right_val->IsString() ? left_val : NULL; } /* Falls through */ 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; case Token::kIFNULL: if (left_val->IsNull()) { return right_val; } return left_val; default: UNREACHABLE(); return NULL; } return NULL; } AstNode* UnaryOpNode::UnaryOpOrLiteral(TokenPosition 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::kNEGATE: case Token::kNOT: case Token::kBIT_NOT: return true; default: return false; } } bool UnaryOpNode::IsPotentiallyConst() const { if (this->operand()->IsLiteralNode() && this->operand()->AsLiteralNode()->literal().IsNull()) { return false; } return this->operand()->IsPotentiallyConst(); } const Instance* UnaryOpNode::EvalConstExpr() const { const Instance* val = this->operand()->EvalConstExpr(); if (val == NULL) { return NULL; } switch (kind_) { 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; } } bool ConditionalExprNode::IsPotentiallyConst() const { return this->condition()->IsPotentiallyConst() && this->true_expr()->IsPotentiallyConst() && this->false_expr()->IsPotentiallyConst(); } const Instance* ConditionalExprNode::EvalConstExpr() const { const Instance* cond = this->condition()->EvalConstExpr(); if ((cond != NULL) && cond->IsBool() && (this->true_expr()->EvalConstExpr() != NULL) && (this->false_expr()->EvalConstExpr() != NULL)) { return cond; } return NULL; } bool ClosureNode::IsPotentiallyConst() const { if (function().IsImplicitStaticClosureFunction()) { return true; } return false; } const Instance* ClosureNode::EvalConstExpr() const { if (!is_deferred_reference_ && 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, StaticGetterSetter::kNoRebind); } return NULL; } const char* UnaryOpNode::TokenName() const { return Token::Str(kind_); } const char* JumpNode::TokenName() const { return Token::Str(kind_); } bool LoadLocalNode::IsPotentiallyConst() const { // Parameters of const constructors are implicitly final and can be // used in initializer expressions. // We can't check here whether the local variable is indeed a parameter, // but this code is executed before any other local variables are // added to the scope. return local().is_final(); } const Instance* LoadLocalNode::EvalConstExpr() const { if (local().IsConst()) { return local().ConstValue(); } return NULL; } AstNode* LoadLocalNode::MakeAssignmentNode(AstNode* rhs) { if (local().is_final()) { return NULL; } return new StoreLocalNode(token_pos(), &local(), rhs); } AstNode* LoadStaticFieldNode::MakeAssignmentNode(AstNode* rhs) { if (field().is_final()) { return NULL; } if (Isolate::Current()->type_checks()) { rhs = new AssignableNode(field().token_pos(), rhs, AbstractType::ZoneHandle(field().type()), String::ZoneHandle(field().name())); } return new StoreStaticFieldNode(token_pos(), Field::ZoneHandle(field().Original()), rhs); } AstNode* InstanceGetterNode::MakeAssignmentNode(AstNode* rhs) { return new InstanceSetterNode(token_pos(), receiver(), field_name(), rhs, is_conditional()); } bool InstanceGetterNode::IsPotentiallyConst() const { return field_name().Equals(Symbols::Length()) && !is_conditional() && receiver()->IsPotentiallyConst(); } const Instance* InstanceGetterNode::EvalConstExpr() const { if (field_name().Equals(Symbols::Length()) && !is_conditional()) { const Instance* receiver_val = receiver()->EvalConstExpr(); if ((receiver_val != NULL) && receiver_val->IsString()) { return &Instance::ZoneHandle(Smi::New(1)); } } return NULL; } AstNode* LoadIndexedNode::MakeAssignmentNode(AstNode* rhs) { return new StoreIndexedNode(token_pos(), array(), index_expr(), rhs, super_class()); } AstNode* StaticGetterNode::MakeAssignmentNode(AstNode* rhs) { Thread* thread = Thread::Current(); Zone* zone = thread->zone(); Isolate* isolate = thread->isolate(); if (is_super_getter()) { ASSERT(receiver() != NULL); const String& setter_name = String::ZoneHandle(zone, Field::LookupSetterSymbol(field_name_)); Function& setter = Function::ZoneHandle(zone); if (!setter_name.IsNull()) { setter = Resolver::ResolveDynamicAnyArgs(zone, cls(), setter_name); } if (setter.IsNull() || setter.is_abstract()) { // No instance setter found in super class chain, // noSuchMethod will be called at runtime. return new StaticSetterNode(token_pos(), receiver(), cls(), field_name_, rhs, StaticGetterSetter::kSuper); } return new StaticSetterNode(token_pos(), receiver(), field_name_, setter, rhs, StaticGetterSetter::kSuper); } if (owner().IsLibraryPrefix()) { const LibraryPrefix& prefix = LibraryPrefix::Cast(owner_); // The parser has already dealt with the pathological case where a // library imports itself. See Parser::ResolveIdentInPrefixScope() ASSERT(field_name_.CharAt(0) != Library::kPrivateIdentifierStart); // If the prefix is not yet loaded, the getter doesn't exist. Return a // setter that will throw a NSME at runtime. if (!prefix.is_loaded()) { return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs, StaticGetterSetter::kStatic); } Object& obj = Object::Handle(zone, prefix.LookupObject(field_name_)); if (obj.IsField()) { const Field& field = Field::ZoneHandle(zone, Field::Cast(obj).raw()); if (!field.is_final()) { if (isolate->type_checks()) { rhs = new AssignableNode(field.token_pos(), rhs, AbstractType::ZoneHandle(zone, field.type()), field_name_); } return new StoreStaticFieldNode(token_pos(), field, rhs); } } // No field found in prefix. Look for a setter function. const String& setter_name = String::Handle(zone, Field::LookupSetterSymbol(field_name_)); if (!setter_name.IsNull()) { obj = prefix.LookupObject(setter_name); if (obj.IsFunction()) { const Function& setter = Function::ZoneHandle(zone, Function::Cast(obj).raw()); ASSERT(setter.is_static() && setter.IsSetterFunction()); return new StaticSetterNode(token_pos(), NULL, field_name_, setter, rhs, StaticGetterSetter::kStatic); } } // No writeable field and no setter found in the prefix. Return a // non-existing setter that will throw an NSM error. return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs, StaticGetterSetter::kStatic); } if (owner().IsLibrary()) { const Library& library = Library::Cast(owner()); Object& obj = Object::Handle(zone, library.ResolveName(field_name_)); if (obj.IsField()) { const Field& field = Field::ZoneHandle(zone, Field::Cast(obj).raw()); if (!field.is_final()) { if (isolate->type_checks()) { rhs = new AssignableNode(field.token_pos(), rhs, AbstractType::ZoneHandle(zone, field.type()), field_name_); } return new StoreStaticFieldNode(token_pos(), field, rhs); } } // No field found in library. Look for a setter function. const String& setter_name = String::Handle(zone, Field::LookupSetterSymbol(field_name_)); if (!setter_name.IsNull()) { obj = library.ResolveName(setter_name); if (obj.IsFunction()) { const Function& setter = Function::ZoneHandle(zone, Function::Cast(obj).raw()); ASSERT(setter.is_static() && setter.IsSetterFunction()); return new StaticSetterNode(token_pos(), NULL, field_name_, setter, rhs, StaticGetterSetter::kStatic); } } // No writeable field and no setter found in the library. Return a // non-existing setter that will throw an NSM error. return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs, StaticGetterSetter::kStatic); } const Function& setter = Function::ZoneHandle(zone, cls().LookupSetterFunction(field_name_)); if (!setter.IsNull() && setter.IsStaticFunction()) { return new StaticSetterNode(token_pos(), NULL, field_name_, setter, rhs, StaticGetterSetter::kStatic); } // 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(zone, 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, StaticGetterSetter::kStatic); } #if defined(DEBUG) const String& getter_name = String::Handle(zone, Field::LookupGetterSymbol(field_name_)); ASSERT(!getter_name.IsNull()); const Function& getter = Function::Handle(zone, cls().LookupStaticFunction(getter_name)); ASSERT(!getter.IsNull() && (getter.kind() == RawFunction::kImplicitStaticFinalGetter)); #endif if (isolate->type_checks()) { rhs = new AssignableNode(field.token_pos(), rhs, AbstractType::ZoneHandle(zone, field.type()), String::ZoneHandle(zone, field.name())); } return new StoreStaticFieldNode(token_pos(), field, rhs); } // Didn't find a static setter or a static field. Make a call to // the non-existent setter to trigger a NoSuchMethodError at runtime. return new StaticSetterNode(token_pos(), NULL, cls(), field_name_, rhs, StaticGetterSetter::kStatic); } 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; } bool StaticGetterNode::IsPotentiallyConst() const { if (is_deferred_reference_) { return false; } 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 false; } return true; } const Instance* StaticGetterNode::EvalConstExpr() const { if (is_deferred_reference_) { return NULL; } const String& getter_name = String::Handle(Field::LookupGetterSymbol(this->field_name())); if (getter_name.IsNull()) { return NULL; } 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 #endif // !defined(DART_PRECOMPILED_RUNTIME)