/** * Executes this check * * @param xp.compiler.ast.Node node * @param xp.compiler.types.Scope scope * @return bool */ public function verify(\xp\compiler\ast\Node $node, \xp\compiler\types\Scope $scope) { $routine = \cast($node, 'xp.compiler.ast.RoutineNode'); $qname = $scope->declarations[0]->name->compoundName() . '::' . $routine->getName(); $empty = $routine->body === null; if ($scope->declarations[0] instanceof \xp\compiler\ast\InterfaceNode) { if (!$empty) { return ['R403', 'Interface methods may not have a body ' . $qname]; } else { if ($routine->modifiers !== MODIFIER_PUBLIC && $routine->modifiers !== 0) { return ['R401', 'Interface methods may only be public ' . $qname]; } } } else { if (Modifiers::isAbstract($routine->modifiers) && !$empty) { return ['R403', 'Abstract methods may not have a body ' . $qname]; } else { if (!Modifiers::isAbstract($routine->modifiers) && $empty) { return ['R401', 'Non-abstract methods must have a body ' . $qname]; } } if ($routine->extension && !Modifiers::isStatic($routine->modifiers)) { return ['E403', 'Extension methods must be static ' . $qname]; } } }
/** * Prints methods - static first, rest then * * @param lang.reflect.Method[] methods */ protected static function printMethods(array $methods) { $i = 0; foreach ($methods as $method) { if (!Modifiers::isStatic($method->getModifiers())) { continue; } Console::writeLine(' ', $method); $i++; } $i && Console::writeLine(); $i = 0; foreach ($methods as $method) { if (Modifiers::isStatic($method->getModifiers())) { continue; } Console::writeLine(' ', $method); $i++; } }
/** * Convert value based on type * * @param lang.Type type * @param [:var] value * @return var */ public function unmarshal($type, $value) { if (null === $type || $type->equals(Type::$VAR)) { // No conversion return $value; } else { if (null === $value) { // Valid for any type return null; } else { if ($type instanceof Primitive) { return $type->cast($this->valueOf($value)); } else { if ($type->equals(XPClass::forName('util.Date'))) { return $type->newInstance($value); } else { if ($type instanceof XPClass) { if ($type->isInstance($value)) { return $value; } foreach ($this->marshallers->keys() as $t) { if ($t->isAssignableFrom($type)) { return $this->marshallers[$t]->unmarshal($type, $value, $this); } } // Check if a public static valueOf() method exists if ($type->hasMethod('valueOf')) { $valueOf = $type->getMethod('valueOf'); if (Modifiers::isStatic($valueOf->getModifiers()) && Modifiers::isPublic($valueOf->getModifiers())) { if (1 === $valueOf->numParameters()) { return $valueOf->invoke(null, [$this->unmarshal($this->paramType($valueOf->getParameter(0)), $value)]); } else { $param = 0; $args = []; foreach ($this->iterableOf($value) as $value) { $args[] = $this->unmarshal($this->paramType($valueOf->getParameter($param++)), $value); } return $valueOf->invoke(null, $args); } } } // Generic approach $return = $type->newInstance(); foreach ($this->iterableOf($value) as $name => $value) { foreach ($this->variantsOf($name) as $variant) { if ($type->hasField($variant)) { $field = $type->getField($variant); $m = $field->getModifiers(); if ($m & MODIFIER_STATIC) { continue; } else { if ($m & MODIFIER_PUBLIC) { if (null !== ($fType = $field->getType())) { $field->set($return, $this->unmarshal($fType, $value)); } else { $field->set($return, $value); } continue 2; } } } if ($type->hasMethod('set' . $variant)) { $method = $type->getMethod('set' . $variant); if ($method->getModifiers() & MODIFIER_PUBLIC) { if (null !== ($param = $method->getParameter(0))) { $method->invoke($return, [$this->unmarshal($param->getType(), $value)]); } else { $method->invoke($return, [$value]); } continue 2; } } } } return $return; } else { if ($type instanceof \lang\ArrayType) { $return = []; foreach ($this->iterableOf($value) as $element) { $return[] = $this->unmarshal($type->componentType(), $element); } return $return; } else { if ($type instanceof \lang\MapType) { $return = []; foreach ($this->iterableOf($value) as $key => $element) { $return[$key] = $this->unmarshal($type->componentType(), $element); } return $return; } else { if ($type->equals(Type::$ARRAY)) { $return = []; foreach ($this->iterableOf($value) as $key => $element) { $return[$key] = $element; } return $return; } else { throw new \lang\FormatException('Cannot convert to ' . \xp::stringOf($type)); } } } } } } } } }
public function staticModifier() { $this->assertTrue(\lang\reflect\Modifiers::isStatic(MODIFIER_STATIC)); }
/** * Generates code for (re)implementation of the (abstract) class methods * of the base class. * * @param lang.XPClass baseClass */ private function generateBaseClassMethods($baseClass) { $bytes = ''; $reservedMethods = \lang\XPClass::forName('lang.Generic')->getMethods(); $reservedMethodNames = array_map(function ($i) { return $i->getName(); }, $reservedMethods); foreach ($baseClass->getMethods() as $m) { // do not overwrite reserved methods, omit static methods if (in_array($m->getName(), $reservedMethodNames) || Modifiers::isStatic($m->getModifiers())) { continue; } // Check for already declared methods, do not redeclare them // implement abstract methods if ($this->overwriteExisting || ($m->getModifiers() & 2) == 2) { if (isset($this->added[$m->getName()])) { continue; } $this->added[$m->getName()] = true; $bytes .= $this->generateMethod($m); } } return $bytes; }
/** * Resolve a static call. Return true if the target is a function * (e.g. key()), a xp.compiler.types.Method instance if it's a static * method (Map::key()). * * @param string name * @return var */ public function resolveStatic($name) { foreach ($this->statics[0] as $lookup => $type) { if (true === $type && $this->importer->hasFunction($lookup, $name)) { return true; } else { if ($type instanceof Types && $type->hasMethod($name)) { $m = $type->getMethod($name); if (\lang\reflect\Modifiers::isStatic($m->modifiers)) { return $m; } } } } return null; }
/** * Emit a class field * * @param xp.compiler.emit.Buffer b * @param xp.compiler.ast.FieldNode field */ protected function emitField($b, $field) { $static = Modifiers::isStatic($field->modifiers); // See whether an initialization is necessary $initializable = false; if ($field->initialization) { if ($field->initialization instanceof Resolveable) { try { $init = $field->initialization->resolve(); $initializable = true; } catch (\lang\IllegalStateException $e) { $this->warn('R100', $e->getMessage(), $field->initialization); $initializable = false; } } if (!$initializable) { $init = new Buffer('', $b->line); $this->enter(new MethodScope(new MethodNode(['name' => '<init>']))); if ($static) { $variable = new StaticMemberAccessNode(new TypeName('self'), $field->name); } else { $variable = new MemberAccessNode(new VariableNode('this'), $field->name); $this->scope[0]->setType(new VariableNode('this'), $this->scope[0]->declarations[0]->name); } $this->emitOne($init, new AssignmentNode(['variable' => $variable, 'expression' => $field->initialization, 'op' => '='])); $init->append(';'); $type = $this->scope[0]->typeOf($variable); $this->leave(); $this->inits[0][$static][] = $init; $this->scope[0]->setType($field->initialization, $type); } // If the field is "var" and we have an initialization, determine // the type from there if ($field->type->isVariable()) { $field->type = $this->scope[0]->typeOf($field->initialization); } } switch ($field->modifiers & (MODIFIER_PUBLIC | MODIFIER_PROTECTED | MODIFIER_PRIVATE)) { case MODIFIER_PRIVATE: $b->append('private '); break; case MODIFIER_PROTECTED: $b->append('protected '); break; default: $b->append('public '); break; } $static && $b->append('static '); $b->append('$' . $field->name); $initializable && $b->append('= ')->append(var_export($init, true)); $b->append(';'); // Meta data. Add field metadata (type, stored in @type annotation, see // lang.reflect.Field and lang.XPClass::detailsForField()). $type = $this->resolveType($field->type); $this->metadata[0][0][$field->name] = $this->meta($field->comment, $field->annotations, [DETAIL_ANNOTATIONS => ['type' => $type->name()]]); // Register type information $f = new Field(); $f->name = $field->name; $f->type = new TypeName($type->name()); $f->modifiers = $field->modifiers; $this->types[0]->addField($f); }