args = $args; $this->is_new = FALSE; $this->is_super = $variable === 'super'; $this->variable = $this->is_super() ? NULL : $variable; $this->soak = $soak; return $this; } function compile_node($options) { if ($this->variable) { $this->variable->front = $this->front; } if (($code = yy_Splat::compile_splatted_array($options, $this->args, TRUE))) { return $this->compile_splat($options, $code); } $args = $this->filter_implicit_objects($this->args); $tmp = array(); foreach ($args as $arg) { $tmp[] = $arg->compile($options, LEVEL_LIST); } $args = implode(', ', $tmp); if ($this->is_super()) { return $this->super_reference($options).'.call(this'.($args ? ', '.$args : '').')'; } else { return ($this->is_new() ? 'new ' : '').$this->variable->compile($options, LEVEL_ACCESS)."({$args})"; } } function compile_super($args, $options) { return $this->super_reference($options).'.call(this'.(count($args) ? ', ' : '').$args.')'; } function compile_splat($options, $splat_args) { if ($this->is_super()) { return $this->super_reference($options).'.apply(this, '.$splat_args.')'; } if ($this->is_new()) { $idt = $this->tab.TAB; return "(function(func, args, ctor) {\n" . "{$idt}ctor.prototype = func.prototype;\n" . "{$idt}var child = new ctor, result = func.apply(child, args), t = typeof result;\n" . "{$idt}return t == \"object\" || t == \"function\" ? result || child : child;\n" . "{$this->tab}})(".$this->variable->compile($options, LEVEL_LIST).", $splat_args, function(){})"; } $base = yy('Value', $this->variable); if (($name = array_pop($base->properties)) && $base->is_complex()) { $ref = $options['scope']->free_variable('ref'); $fun = "($ref = ".$base->compile($options, LEVEL_LIST).')'.$name->compile($options).''; } else { $fun = $base->compile($options, LEVEL_ACCESS); $fun = preg_match(SIMPLENUM, $fun) ? "($fun)" : $fun; if ($name) { $ref = $fun; $fun .= $name->compile($options); } else { $ref = NULL; } } $ref = $ref === NULL ? 'null' : $ref; return "{$fun}.apply({$ref}, {$splat_args})"; } function is_new($set = NULL) { if ($set !== NULL) { $this->is_new = !! $set; } return $this->is_new; } function is_super() { return $this->is_super; } function filter_implicit_objects($list) { $nodes = array(); foreach ($list as $node) { if ( ! ($node->is_object() && $node->base->generated)) { $nodes[] = $node; continue; } $obj = NULL; foreach ($node->base->properties as $prop) { if (($prop instanceof yy_Assign) || $prop instanceof yy_Comment) { if ( ! $obj) { $nodes[] = ($obj = yy('Obj', array(), TRUE)); } $obj->properties[] = $prop; } else { $nodes[] = $prop; $obj = NULL; } } } return $nodes; } function new_instance() { $base = isset($this->variable->base) ? $this->variable->base : $this->variable; if (($base instanceof yy_Call) && ! $base->is_new()) { $base->new_instance(); } else { $this->is_new = TRUE; } return $this; } function super_reference($options) { $method = $options['scope']->method; if ($method === NULL) { throw new SyntaxError('cannot call super outside of a function.'); } $name = isset($method->name) ? $method->name : NULL; if ($name === NULL) { throw new SyntaxError('cannot call super on an anonymous function.'); } if (isset($method->klass) && $method->klass) { $accesses = array(yy('Access', yy('Literal', '__super__'))); if (isset($method->static) && $method->static) { $accesses[] = yy('Access', yy('Literal', 'constructor')); } $accesses[] = yy('Access', yy('Literal', $name)); return yy('Value', yy('Literal', $method->klass), $accesses)->compile($options); } else { return $name.'.__super__.constructor'; } } function unfold_soak($options) { if ($this->soak) { if ($this->variable) { if ($ifn = unfold_soak($options, $this, 'variable')) { return $ifn; } $tmp = yy('Value', $this->variable); list($left, $rite) = $tmp->cache_reference($options); } else { $left = yy('Literal', $this->super_reference($options)); $rite = yy('Value', $left); } $rite = yy('Call', $rite, $this->args); $rite->is_new($this->is_new()); $left = yy('Literal', 'typeof '.$left->compile($options).' === "function"'); return yy('If', $left, yy('Value', $rite), array('soak' => TRUE)); } $call = $this; $list = array(); while (TRUE) { if ($call->variable instanceof yy_Call) { $list[] = $call; $call = $call->variable; continue; } if ( ! ($call->variable instanceof yy_Value)) { break; } $list[] = $call; if ( ! (($call = $call->variable->base) instanceof yy_Call)) { break; } } foreach (array_reverse($list) as $call) { if (isset($ifn)) { if ($call->variable instanceof yy_Call) { $call->variable = $ifn; } else { $call->variable->base = $ifn; } } $ifn = unfold_soak($options, $call, 'variable'); } return isset($ifn) ? $ifn : NULL; } } ?>