/**
  * 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)
 {
     $access = \cast($node, 'xp.compiler.ast.ArrayAccessNode');
     $type = $scope->typeOf($access->target);
     $result = TypeName::$VAR;
     $message = null;
     if ($type->isArray()) {
         $result = $type->arrayComponentType();
     } else {
         if ($type->isMap()) {
             $result = $type->mapComponentType();
         } else {
             if ($type->isClass()) {
                 $ptr = new TypeInstance($scope->resolveType($type));
                 if ($ptr->hasIndexer()) {
                     $result = $ptr->getIndexer()->type;
                 } else {
                     $message = ['T305', 'Type ' . $ptr->name() . ' does not support offset access'];
                 }
             } else {
                 if ($type->isVariable()) {
                     $message = ['T203', 'Array access (var)' . $access->hashCode() . ' verification deferred until runtime'];
                 } else {
                     if ('string' === $type->name) {
                         $result = $type;
                     } else {
                         $message = ['T305', 'Using array-access on unsupported type ' . $type->toString()];
                     }
                 }
             }
         }
     }
     $scope->setType($access, $result);
     return $message;
 }
Exemple #2
0
 /**
  * Emit member access
  *
  * @param   xp.compiler.emit.Buffer b
  * @param   xp.compiler.ast.MemberAccessNode access
  */
 public function emitMemberAccess($b, $access)
 {
     $mark = $b->mark();
     $this->emitOne($b, $access->target);
     $type = $this->scope[0]->typeOf($access->target);
     // Overload [...].length
     if ($type->isArray() && 'length' === $access->name) {
         $b->insert('sizeof(', $mark);
         $b->append(')');
         $this->scope[0]->setType($access, new TypeName('int'));
         return;
     }
     // Manually verify as we can then rely on call target type being available
     if (!$this->checks->verify($access, $this->scope[0], $this, true)) {
         return;
     }
     // Navigation operator
     if ($access->nav) {
         $var = $this->tempVar();
         $b->insert('(NULL === (' . $var . '=', $mark);
         $b->append(') ? NULL : ')->append($var)->append('->');
         $b->append($access->name);
         $b->append(')');
     } else {
         // Rewrite for unsupported syntax
         // - new Person().name to (new Person()).name
         // - (<expr>).name to an inline ternary
         if ($access->target instanceof InstanceCreationNode) {
             $b->insert('(', $mark);
             $b->append(')');
         } else {
             if (!$access->target instanceof ArrayAccessNode && !$access->target instanceof MethodCallNode && !$access->target instanceof MemberAccessNode && !$access->target instanceof VariableNode && !$access->target instanceof StaticMemberAccessNode && !$access->target instanceof StaticMethodCallNode) {
                 $var = $this->tempVar();
                 $b->insert('(NULL === (' . $var . '=', $mark);
                 $b->append(')) ? NULL : ' . $var);
             }
         }
         $b->append('->' . $access->name);
     }
     // Record type
     $ptr = new TypeInstance($this->resolveType($type));
     if ($ptr->hasField($access->name)) {
         $result = $ptr->getField($access->name)->type;
     } else {
         if ($ptr->hasProperty($access->name)) {
             $result = $ptr->getProperty($access->name)->type;
         } else {
             $result = TypeName::$VAR;
         }
     }
     $this->scope[0]->setType($access, $result);
 }