/**
  * 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.MemberAccessNode');
     // Verify type
     // * var: Might have method
     // * primitive, array, map, int: Definitely don't have fields
     $type = $scope->typeOf($access->target);
     if ($type->isVariable()) {
         return ['T203', 'Member access (var).' . $access->name . '() verification deferred until runtime'];
     } else {
         if (!$type->isClass()) {
             return ['T305', 'Using member access on unsupported type ' . $type->compoundName()];
         }
     }
     // Verify target method exists
     $target = new \xp\compiler\types\TypeInstance($scope->resolveType($type));
     if ($target->hasField($access->name)) {
         $member = $target->getField($access->name);
     } else {
         if ($target->hasProperty($access->name)) {
             $member = $target->getProperty($access->name);
         } else {
             return ['T404', 'No such field $' . $access->name . ' in ' . $target->name()];
         }
     }
     // Verify visibility
     if (!($member->modifiers & MODIFIER_PUBLIC)) {
         $enclosing = $scope->resolveType($scope->declarations[0]->name);
         if ($member->modifiers & MODIFIER_PRIVATE && !$enclosing->equals($target) || $member->modifiers & MODIFIER_PROTECTED && !($enclosing->equals($target) || $enclosing->isSubclassOf($target))) {
             return ['T403', sprintf('Accessing %s %s::$%s from %s', implode(' ', \lang\reflect\Modifiers::namesOf($member->modifiers)), $target->name(), $member->name, $enclosing->name())];
         }
     }
 }
 /**
  * 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;
 }
 /**
  * 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)
 {
     $v = \cast($node, 'xp.compiler.ast.VariableNode');
     if (!$scope->getType($v)) {
         return ['V404', 'Uninitialized variable ' . $v->name];
     }
 }
 /**
  * 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)
 {
     $arm = cast($node, 'xp.compiler.ast.ArmNode');
     foreach ($arm->initializations as $i => $init) {
         $type = $scope->resolveType($scope->typeOf($init), false);
         if (!$type->isSubclassOf(self::$closeable)) {
             return ['A403', 'Type ' . $type->name() . ' for assignment #' . ($i + 1) . ' in ARM block is not closeable'];
         }
     }
 }
 /**
  * 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)
 {
     $call = \cast($node, 'xp.compiler.ast.MethodCallNode');
     // Verify type
     // * var: Might have method
     // * primitive, array, map, int: Definitely don't have methods
     $type = $scope->typeOf($call->target);
     if ($type->isVariable()) {
         return ['T203', 'Member call (var).' . $call->name . '() verification deferred until runtime'];
     } else {
         if (!$type->isClass()) {
             return ['T305', 'Using member calls on unsupported type ' . $type->compoundName()];
         }
     }
     return $this->verifyMethod($type, $call->name, $scope);
 }
Esempio n. 6
0
 /**
  * Entry point
  *
  * @param   xp.compiler.ast.ParseTree tree
  * @param   xp.compiler.types.Scope scope
  * @return  xp.compiler.Result
  */
 public function emit(ParseTree $tree, Scope $scope)
 {
     $bytes = new Buffer('', 1);
     array_unshift($this->local, []);
     array_unshift($this->temp, 0);
     array_unshift($this->scope, $scope->enter(new CompilationUnitScope()));
     $this->scope[0]->importer = $this->nativeImporter;
     $this->scope[0]->declarations = [$tree->declaration];
     $this->scope[0]->package = $tree->package;
     // Functions from lang.base.php
     $this->scope[0]->statics = [0 => [], 'newinstance' => true, 'with' => true, 'create' => true, 'raise' => true, 'delete' => true, 'cast' => true, 'is' => true, 'this' => true, 'isset' => true, 'unset' => true, 'empty' => true, 'eval' => true, 'typeof' => true, 'nameof' => true, 'include' => true, 'require' => true, 'include_once' => true, 'require_once' => true];
     $this->cat && $this->cat->infof('== Enter %s ==', basename($tree->origin));
     // Import and declarations
     $t = null;
     $this->scope[0]->addResolved('self', new TypeDeclaration($tree));
     // FIXME: for import self
     $this->emitAll($bytes, (array) $tree->imports);
     while ($this->scope[0]->declarations) {
         array_unshift($this->types, new CompiledType());
         $decl = current($this->scope[0]->declarations);
         $this->local[0][$decl->name->name] = true;
         $this->emitOne($bytes, $decl);
         array_shift($this->scope[0]->declarations);
         $t || ($t = $this->types[0]);
     }
     // Load used classes
     $this->emitUses($bytes, $this->scope[0]->used);
     // Leave scope
     array_shift($this->local);
     $this->leave();
     // Check on errors
     $this->cat && $this->cat->infof('== %s: %d error(s), %d warning(s) ==', basename($tree->origin), sizeof($this->messages['errors']), sizeof($this->messages['warnings']));
     if ($this->messages['errors']) {
         throw new FormatException('Errors emitting ' . $tree->origin . ': ' . \xp::stringOf($this->messages));
     }
     // Finalize
     return new Result($t, $bytes);
 }