/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package macromedia.asc.semantics;
import macromedia.asc.embedding.avmplus.*;
import macromedia.asc.embedding.ErrorConstants;
import macromedia.asc.parser.*;
import macromedia.asc.util.*;
import macromedia.asc.util.graph.*;
import java.util.*;
import static macromedia.asc.parser.Tokens.*;
import static macromedia.asc.semantics.Slot.*;
import static macromedia.asc.embedding.avmplus.RuntimeConstants.*;
import static macromedia.asc.embedding.avmplus.ActionBlockConstants.*;
/**
* FlowAnalyzer
*
* @author Jeff Dyer
*/
public final class FlowAnalyzer extends Emitter implements Evaluator, ErrorConstants
{
private static final String INTRINSIC = "intrinsic".intern();
private static final String INTERNAL = "internal".intern();
private static final String PUBLIC = "public".intern();
private static final String PRIVATE = "private".intern();
private static final String PROTECTED = "protected".intern();
private static final String PROTOTYPE = "prototype".intern();
private static final String STATIC = "static".intern();
private Names interfaceMethods = null;
private boolean define_cv;
private boolean errorNodeSeen = false;
private boolean endpoint_dominator_is_set;
private boolean found_circular_or_duplicate_class_definition;
private Node endpoint_dominator;
class CaseList extends ObjectList<Node>
{
boolean hasDefault;
}
private ObjectList<CaseList> case_exprs = new ObjectList<CaseList>();
private ObjectList<ObjectList<FunctionCommonNode>> fexprs_sets = new ObjectList<ObjectList<FunctionCommonNode>>();
private ObjectList<ObjectList<ClassDefinitionNode>> clsdefs_sets = new ObjectList<ObjectList<ClassDefinitionNode>>();
private ObjectList<ObjectList<Node>> instanceinits_sets = new ObjectList<ObjectList<Node>>();
private ObjectList<ObjectList<FunctionCommonNode>> staticfexprs_sets = new ObjectList<ObjectList<FunctionCommonNode>>();
private ObjectList<Namespaces> usednamespaces_sets = new ObjectList<Namespaces>();
private ObjectList<Namespaces> used_def_namespaces_sets = new ObjectList<Namespaces>();
private ObjectList<Multinames> importednames_sets = new ObjectList<Multinames>();
private ObjectList<ObjectValue> private_namespaces = new ObjectList<ObjectValue>();
private ObjectList<ObjectValue> default_namespaces = new ObjectList<ObjectValue>();
private ObjectList<ObjectValue> public_namespaces = new ObjectList<ObjectValue>();
private ObjectList<ObjectValue> protected_namespaces = new ObjectList<ObjectValue>();
private ObjectList<ObjectValue> static_protected_namespaces = new ObjectList<ObjectValue>();
private IntList max_params_stack = new IntList();
private IntList max_locals_stack = new IntList();
private IntList max_temps_stack = new IntList();
private ObjectList<String> fun_name_stack = new ObjectList<String>();
private IntList with_used_stack = new IntList();
private IntList exceptions_used_stack = new IntList();
private ObjectList<NumberUsage> number_usage_stack = new ObjectList<NumberUsage>();
private ObjectList<String> region_name_stack = new ObjectList<String>();
private String package_name = "";
private ObjectList<Boolean> import_context = new ObjectList<Boolean>();
private ObjectList<Boolean> strict_context = new ObjectList<Boolean>();
private IntList this_contexts = new IntList();
private IntList super_context = new IntList();
private boolean resolveInheritance;
// unresolved defintion imports
private ObjectList<Set<ReferenceValue>> import_def_unresolved_sets = new ObjectList<Set<ReferenceValue>>();
// unresolved package imports
private ObjectList<Set<ReferenceValue>> package_unresolved_sets = new ObjectList<Set<ReferenceValue>>();
// unresolved namespaces
private ObjectList<Set<ReferenceValue>> ns_unresolved_sets = new ObjectList<Set<ReferenceValue>>();
// unresolved "extends" and "implements"
private ObjectList<Set<ReferenceValue>> fa_unresolved_sets = new ObjectList<Set<ReferenceValue>>();
// unresolved variable/function/parameter types
private ObjectList<Set<ReferenceValue>> ce_unresolved_sets = new ObjectList<Set<ReferenceValue>>();
private ObjectList<Set<ReferenceValue>> body_unresolved_sets = new ObjectList<Set<ReferenceValue>>();
// unresolved member expressions
private ObjectList<Set<ReferenceValue>> rt_unresolved_sets = new ObjectList<Set<ReferenceValue>>();
// temporary container for accumulating unresolved ReferenceValues...
private Set<ReferenceValue> unresolved = new HashSet<ReferenceValue>();
// this is for the ProgramNode's allocateTemp() value because FA processes ProgramNode twice.
private int programNode_temp;
ObjectValue local_file_namespace;
private FlowGraphEmitter getEmitter()
{
return (FlowGraphEmitter) impl;
}
private int loop_index;
/***** ALL THE FOLLOWING ARE NEVER USED *******
private boolean traverse_list_right_to_left = false;
private boolean c_call_sequence = false;
private int allocateFixedTemp()
{
// ISSUE why not return the allocTemp value?
allocateTemp();
return cur_fixed_temp_count++;
}
private void freeFixedTemp(int t)
{
//printf("\nfreeTemp() cur_temp_count = %d", cur_temp_count);
--cur_fixed_temp_count;
freeTemp(t);
}
public void push_args_first()
{
push_args_first(false);
}
public void push_args_first(boolean b)
{
c_call_sequence = b;
}
public void push_args_right_to_left()
{
push_args_right_to_left(false);
}
public void push_args_right_to_left(boolean b)
{
traverse_list_right_to_left = b;
}
**********************************************/
// 3rd Edition features
static boolean debug = false;
public FlowAnalyzer(Emitter emitter)
{
super(emitter);
loop_index = 0;
define_cv = false;
endpoint_dominator_is_set = false;
found_circular_or_duplicate_class_definition = false;
package_name = "";
resolveInheritance = true;
import_context.push_back(false);
}
public boolean checkFeature(Context cx, Node node)
{
// If the node->block is null and we are actually processing
// code inside the normal flow of the method, then get and
// save the current block. This is necessary because we pre-
// process definition initializers before we start doing
// proper flow analysis.
if (node.block == null && doingMethod())
{
node.block = getEmitter().getBlock();
}
return true;
}
// Expression evaluators
public Value evaluate(Context cx, IdentifierNode node)
{
// IDENTIFIER_EVAL_PROLOG
if( node.ref == null)
{
Namespaces namespaces;
// Add the namespaces associated with
namespaces = usednamespaces_sets.back();
// And then add the namespaces associated with node.name, if any
Multinames importednames = importednames_sets.back();
Namespaces nss = importednames.get(node.name);
if( nss != null )
{
// make and modify a copy so we don't affect the set of open namespaces
namespaces = new Namespaces(namespaces);
namespaces.addAll(nss);
}
node.ref = new ReferenceValue(cx, null,node.name,namespaces);
node.ref.setIsAttributeIdentifier(node.isAttr());
// If there is a set of names
node.ref.setPosition(node.pos());
}
// IDENTIFIER_EVAL_EPILOG
return node.ref;
}
public Value evaluate( Context cx, QualifiedIdentifierNode node )
{
// IDENTIFIER_EVAL_PROLOG
if( node.ref == null)
{
ObjectValue ns = null;
Namespaces nss = null; // for protected
if( node.qualifier != null)
{
AttributeListNode attrs = ((node.qualifier instanceof AttributeListNode) ? (AttributeListNode)node.qualifier : null);
if( attrs != null)
{
if( attrs.namespaces.size() > 0 )
{
ns = attrs.namespaces.get(0); // pick any one
}
else
{
cx.internalError(node.pos(), "internal error: invalid qualifier in QualifiedIdentifierNode");
ns = cx.publicNamespace(); // to fail gracefully
}
}
else
{
Value ref = null;
MemberExpressionNode memb = ((node.qualifier instanceof MemberExpressionNode) ? (MemberExpressionNode)node.qualifier : null);
if( memb != null )
{
if( memb.isAny() )
{
ref = cx.anyNamespace();
}
else
{
IdentifierNode id = memb.selector.getIdentifier();
if( id != null && (id.name == INTERNAL) )
{
ns = default_namespaces.back();
}
else
if( id != null && (id.name == PRIVATE) )
{
if( private_namespaces.size() == 0 )
{
cx.error(node.qualifier.pos(), kError_InvalidPrivate);
}
else
{
ns = private_namespaces.back();
}
}
else
if( id != null && (id.name == PROTECTED) )
{
if( static_protected_namespaces.size() == 0 )
{
cx.error(node.qualifier.pos(), kError_InvalidProtected);
}
else
{
nss = new Namespaces();
nss.push_back(static_protected_namespaces.back());
if( protected_namespaces.size() != 0 )
{
for( ObjectValue n : usednamespaces_sets.back() )
{
if( n.getNamespaceKind() == Context.NS_PROTECTED )
{
nss.push_back(n); // kind of a hack, but can't think of a better way to get the set of open protected namespaces
}
}
}
}
}
else
if( id != null && (id.name == PUBLIC) )
{
ns = public_namespaces.back();
}
else
{
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
memb.evaluate(cx,this);
ref = memb.ref;
ns_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
}
}
}
if (node.qualifier instanceof LiteralStringNode) // this case for package qualified references only?
{
LiteralStringNode lsn = (LiteralStringNode)node.qualifier;
ns = cx.getNamespace(lsn.value);
}
else if( ref != null )
{
Value value = ref.getValue(cx);
ns = ((value instanceof ObjectValue) ? (ObjectValue) value : null);
if( ns == null )
{
return null;
// cx.error(node.qualifier.pos(), kError_UndefinedNamespace );
}
}
else
if( ns == null && nss == null )
{
cx.error(node.qualifier.pos(), kError_UndefinedNamespace);
}
}
}
else
{ // if there is no qualifier, then it an internal definition (e.g. no attributes)
ns = default_namespaces.back();
}
if( !node.name.equals("") ) // otherwise it is a QualifiedExpression with no name and no corresponding reference value
{
if( nss != null /* protected = multiname */ )
{
node.ref = new ReferenceValue(cx,null,node.name,nss);
}
else
{
node.ref = new ReferenceValue(cx,null,node.name,ns);
}
node.ref.setIsAttributeIdentifier(node.isAttr());
node.ref.setPosition(node.pos());
}
else
if( node instanceof QualifiedExpressionNode )
{
// Save the reserved namespace as a namespace set to be used later
// to create a constant multiname
QualifiedExpressionNode qen = (QualifiedExpressionNode)node;
if( ns != null )
{
qen.nss = new Namespaces();
qen.nss.push_back(ns);
}
else
{
qen.nss = nss;
}
}
}
// IDENTIFIER_EVAL_EPILOG
return node.ref;
}
public Value evaluate( Context cx, QualifiedExpressionNode node )
{
// IDENTIFIER_EVAL_PROLOG
if( node.ref == null)
{
evaluate(cx,(QualifiedIdentifierNode)node);
node.expr.evaluate(cx,this);
}
// IDENTIFIER_EVAL_EPILOG
return node.ref;
}
public Value evaluate(Context cx, ThisExpressionNode node)
{
if (debug)
{
System.out.print("\n// +ThisExpressionNode");
}
getEmitter().AddStmtToBlock(node.toString());
// ERROR: 'this' is only allowed in global and instance functions
// (including methods and accessors). It is not allowed in class
// static and package methods.
switch( this_contexts.last() )
{
case error_this:
case cinit_this:
case package_this:
cx.error(node.pos(), kError_ThisUsedInStaticFunction);
break;
case init_this:
cx.error(node.pos(), kError_ThisUsedInInitializer);
break;
default:
// valid use of this
break;
}
if( debug ) System.out.print("\n// -ThisExpressionNode");
return null;
}
public Value evaluate(Context cx, LiteralBooleanNode node)
{
if (debug)
{
System.out.print("\n// +LiteralBoolean");
}
getEmitter().AddStmtToBlock(node.toString());
ObjectValue val = node.value?cx.booleanTrue():cx.booleanFalse();
if (debug)
{
System.out.print("\n// -LiteralBoolean");
}
return val;
}
public Value evaluate(Context cx, LiteralNullNode node)
{
if (debug)
{
System.out.print("\n// +LiteralNull");
}
getEmitter().AddStmtToBlock(node.toString());
if (debug)
{
System.out.print("\n// -LiteralNull");
}
return null;
}
public Value evaluate(Context cx, LiteralNumberNode node)
{
if (debug)
{
System.out.print("\n// +LiteralNumber");
}
node.numberUsage = number_usage_stack.last();
getEmitter().AddStmtToBlock(node.toString());
if (debug)
{
System.out.print("\n// -LiteralNumber");
}
return null;
}
public Value evaluate(Context cx, LiteralStringNode node)
{
if (debug)
{
System.out.print("\n// +LiteralString");
}
getEmitter().AddStmtToBlock(node.toString());
if (debug)
{
System.out.print("\n// -LiteralString");
}
return null;
}
public Value evaluate(Context cx, LiteralRegExpNode node)
{
if (debug)
{
System.out.print("\n// +LiteralRegExp");
}
getEmitter().AddStmtToBlock(node.toString());
if (debug)
{
System.out.print("\n// -LiteralRegExp");
}
return null;
}
public Value evaluate(Context cx, LiteralObjectNode node)
{
if (debug)
{
System.out.print("\n// +LiteralObject");
}
getEmitter().AddStmtToBlock(node.toString());
if (node.fieldlist != null)
{
node.fieldlist.evaluate(cx, this);
}
if (debug)
{
System.out.print("\n// -LiteralObject");
}
return null;
}
public Value evaluate(Context cx, LiteralFieldNode node)
{
if (debug)
{
System.out.print("\n// +LiteralField");
}
int t = allocateTemp();
getEmitter().AddStmtToBlock(node.toString());
if (node.name != null)
{
// For LiteralFieldNode's with a name of type
// IdentifierNode, ReferenceValue.findUnqualified() always
// returns false, so speed up the process by creating a
// ReferenceValue with no namespaces.
if (node.name instanceof IdentifierNode)
{
IdentifierNode identifier = (IdentifierNode) node.name;
Namespaces namespaces = new Namespaces();
ReferenceValue referenceValue = new ReferenceValue(cx, null, identifier.name, namespaces);
referenceValue.setIsAttributeIdentifier(false);
referenceValue.setPosition(node.pos());
node.ref = referenceValue;
}
else
{
Value value = node.name.evaluate(cx,this);
node.ref = ((value instanceof ReferenceValue) ? (ReferenceValue)value : null);
}
}
node.value.evaluate(cx, this);
freeTemp(t);
if (debug)
{
System.out.print("\n// -LiteralField");
}
return node.ref;
}
public Value evaluate(Context cx, LiteralArrayNode node)
{
if (debug)
{
System.out.print("\n// +LiteralArray");
}
getEmitter().AddStmtToBlock(node.toString());
if (node.elementlist != null)
{
node.elementlist.evaluate(cx, this);
}
if (debug)
{
System.out.print("\n// -LiteralArray");
}
return null;
}
public Value evaluate(Context cx, LiteralVectorNode node)
{
if (debug)
{
System.out.print("\n// +LiteralVector");
}
getEmitter().AddStmtToBlock(node.toString());
node.type.evaluate(cx, this);
if (node.elementlist != null)
{
node.elementlist.evaluate(cx, this);
}
if (debug)
{
System.out.print("\n// -LiteralVector");
}
return null;
}
public Value evaluate(Context cx, MemberExpressionNode node)
{
if (debug)
{
System.out.print("\n// +MemberExpression");
}
/* If the member expression has a base, evaluate it and try to
* bind it to its slot. If it binds, then get the slot
* type and then the prototype of that type. The prototype will
* be the compile-time stand-in for the instance.
*/
ObjectValue base = null;
// Simple reference
if (node.base == null)
{
Value value = node.selector.evaluate(cx,this);
node.ref = ((value instanceof ReferenceValue) ? (ReferenceValue)value : null);
// This is an alias to node.<selector>.ref, so the
// evaluator for node.<selector> has access to the
// same info
// Simple references always have a node.ref, unless they
// are a function literal.
if( node.ref != null )
{
Slot slot = node.ref.getSlot(cx,(node.ref.getKind()==SET_TOKEN?SET_TOKEN:GET_TOKEN));
node.ref.slot = null; // Don't bind to this slot, since it could be hidden by another slot later
if (slot == null)
{
// System.err.println("Unresolved symbol '" + node.ref.toMultiName() + "' on line " + (cx.getInputLine(node.selector.pos()) + 1) + " in " + cx.getErrorOrigin());
unresolved.add(node.ref);
}
}
}
Value val = null;
// check again, in case the base got added above
if( node.base != null )
{
val = node.base.evaluate(cx, this);
val = val != null ? val.getValue(cx) : null;
base = ((val instanceof ObjectValue) ? (ObjectValue)val : null);
// It's a literal reference
node.selector.base = (base != null) ? base : ObjectValue.undefinedValue;
val = node.selector.evaluate(cx, this);
node.ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
if (node.ref != null)
{
if (base != null && base == cx.globalScope())
{
// Issue: for this to work, we need to be able to evaluate 'this' amoung
// other values.
node.ref.setScopeIndex(0);
node.base = null;
// Don't need to set the base of ref since we know its the global
}
}
else // It's a dynamic reference (bracket only)
{
// Nothing to do right now
}
}
// special case: if selector is not a get expr, then the result is not an lvalue
if( node.selector instanceof GetExpressionNode || node.selector instanceof ApplyTypeExprNode)
{
val = node.ref;
}
else
{
val = null;
}
if (node.base instanceof SuperExpressionNode)
{
adjustProtectedNamespace(cx, node.ref);
}
if (debug)
{
System.out.print("\n// -MemberExpression");
}
return val;
}
public Value evaluate(Context cx, ApplyTypeExprNode node)
{
if( node.ref != null )
{
// Already evaluated this node
return node.ref;
}
if (debug)
{
System.out.print("\n// +ApplyTypeExpression");
}
getEmitter().AddStmtToBlock(node.toString());
if (node.typeArgs != null)
{
node.typeArgs.evaluate(cx, this);
}
Value value = node.expr.evaluate(cx,this);
if( node.expr instanceof IdentifierNode )
{
node.ref = ((value instanceof ReferenceValue) ? (ReferenceValue)value : null);
if( node.typeArgs != null && node.typeArgs.values != null )
{
ObjectList<ReferenceValue> typerefs = new ObjectList<ReferenceValue>();
for ( Value v : node.typeArgs.values )
{
if( v instanceof ReferenceValue )
typerefs.add((ReferenceValue)v);
}
if( typerefs.size() != node.typeArgs.values.size() )
node.ref = null; //Something didn't resolve to a reference
if( node.ref != null )
node.ref.addTypeParam(typerefs.at(0));
}
}
else
{
node.ref = null;
}
if (debug)
{
System.out.print("\n// -ApplyTypeExpression");
}
return node.ref;
}
public Value evaluate(Context cx, CallExpressionNode node)
{
if( node.ref != null )
{
// Already evaluated this node
return node.ref;
}
if (debug)
{
System.out.print("\n// +CallExpression");
}
int t = allocateTemp();
getEmitter().AddStmtToBlock(node.toString());
if (node.args != null)
{
node.args.evaluate(cx, this);
}
Value value = node.expr.evaluate(cx,this);
if( node.expr instanceof IdentifierNode )
{
node.ref = ((value instanceof ReferenceValue) ? (ReferenceValue)value : null);
}
else
{
node.ref = null;
}
if( node.ref != null && "Vector$object".equals(node.ref.name) && node.ref.namespaces.contains(cx.publicNamespace()))
{
if(node.expr instanceof TypeIdentifierNode)
{
TypeIdentifierNode tin = (TypeIdentifierNode)node.expr;
int args_size = node.args != null ? node.args.size():0;
ObjectList<Node> new_args = new ObjectList<Node>(args_size+1);
new_args.push_back(tin.typeArgs.items.at(0));
if( node.args != null )
{
new_args.addAll(node.args.items);
}
node.args = cx.getNodeFactory().argumentList(null, null);
node.args.items = new_args;
}
}
if( node.isAttributeIdentifier() )
{
cx.error(node.pos()-2, kError_AttributesAreNotCallable);
}
if (node.ref != null )
{
node.ref.setBase(node.base); // inherited attribute
}
freeTemp(t);
return node.ref;
}
public Value evaluate(Context cx, InvokeNode node)
{
if (debug)
{
System.out.print("\n// +Invoke");
}
getEmitter().AddStmtToBlock(node.toString());
node.ref = new ReferenceValue(cx, null, node.name, cx.publicNamespace()/*used_namespaces*/);
node.ref.setKind(EMPTY_TOKEN);
node.ref.setBase(node.base); // inherited attribute
node.ref.setPosition(node.pos());
if (node.args != null)
{
node.args.evaluate(cx, this);
}
if (debug)
{
System.out.print("\n// -Invoke");
}
return null;
}
public Value evaluate(Context cx, SetExpressionNode node)
{
getEmitter().AddStmtToBlock(node.toString());
int t = allocateTemp();
Value value = node.expr.evaluate(cx,this);
node.ref = ((value instanceof ReferenceValue) ? (ReferenceValue)value : null);
if (node.ref != null)
{
if( node.base != null )
{
node.ref.setBase(node.base); // inherited from member expression
}
// if in a with block, we can't trust the resolution of the slot
if (cx.statics.withDepth == -1)
{
node.ref.getSlot(cx, SET_TOKEN);
node.gen_bits = getEmitter().NewDef(node);
}
}
if (node.ref == null)
{
node.args.evaluate(cx, this);
}
else
{
node.args.evaluate(cx, this);
if (cx.statics.withDepth == -1)
{
Slot slot = null;
node.ref.setBase(node.base); // inherited attribute
slot = node.ref.getSlot(cx, GET_TOKEN);
node.ref.setKind(SET_TOKEN);
if (slot != null)
{
slot.addDefBits(node.gen_bits);
}
}
}
freeTemp(t);
return node.ref;
}
public Value evaluate(Context cx, DeleteExpressionNode node)
{
if (debug)
{
System.out.print("\n// +DeleteExpression");
}
getEmitter().AddStmtToBlock(node.toString());
Value val = node.expr.evaluate(cx,this);
node.ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
if (node.ref != null)
{
node.ref.setBase(node.base); // inherited attribute
node.ref.setKind(GET_TOKEN);
}
return node.ref;
}
public Value evaluate(Context cx, GetExpressionNode node)
{
if (debug)
{
System.out.print("\n// +GetExpression");
}
getEmitter().AddStmtToBlock(node.toString());
Value val = node.expr.evaluate(cx,this);
node.ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
if (node.ref != null && node.base != null )
{
node.ref.setBase(node.base); // inherited attribute
node.ref.setKind(GET_TOKEN);
}
if (debug)
{
System.out.print("\n// -GetExpression");
}
return node.ref;
}
public Value evaluate(Context cx, IncrementNode node)
{
if (debug)
{
System.out.print("\n// +IncrementNode");
}
int t1 = allocateTemp(); // approximation of actual temp usage.
int t2 = allocateTemp();
int t3 = allocateTemp();
node.numberUsage = number_usage_stack.last();
getEmitter().AddStmtToBlock(node.toString());
Value val = node.expr.evaluate(cx,this);
node.ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
if (node.ref != null)
{
node.ref.setBase(node.base); // inherited attribute
node.ref.getSlot(cx, GET_TOKEN);
}
freeTemp(t3);
freeTemp(t2);
freeTemp(t1);
if (debug)
{
System.out.print("\n// -IncrementNode");
}
return node.ref;
}
/*
* There are two kinds of unary operators: those that operate on references;
* and those that operate on values. The reference operators (Get,Put,Delete,Call,
* Construct are implemented as methods in the ObjectValue. The value operators
* are implemented as methods of the global object.
*/
public Value evaluate(Context cx, UnaryExpressionNode node)
{
if (debug)
{
System.out.print("\n// +UnaryExpressionNode");
}
getEmitter().AddStmtToBlock(node.toString());
node.expr.evaluate(cx, this);
node.numberUsage = number_usage_stack.last();
if (debug)
{
System.out.print("\n// -UnaryExpressionNode");
}
return null;
}
public Value evaluate(Context cx, BinaryExpressionNode node)
{
if (debug)
{
System.out.print("\n// +BinaryExpressionNode");
}
getEmitter().AddStmtToBlock(node.toString());
node.lhs.evaluate(cx, this);
node.rhs.evaluate(cx, this);
node.numberUsage = number_usage_stack.last();
if (debug)
{
System.out.print("\n// -BinaryExpressionNode");
}
return null;
}
public Value evaluate(Context cx, ConditionalExpressionNode node)
{
if (debug)
{
System.out.print("\n// +ConditionalExpressionNode");
}
node.condition.evaluate(cx, this); // leaves value of governing expr on stack
If(IF_false); // if false, jump past then actions
node.thenexpr.evaluate(cx, this);
Else(); // jump past else actions
PatchIf(getIP()); // patch target of if jump
node.elseexpr.evaluate(cx, this);
PatchElse(getIP()); // patch target of else jumps
if (debug)
{
System.out.print("\n// -ConditionalExpressionNode");
}
return null;
}
public Value evaluate(Context cx, ArgumentListNode node)
{
if (debug)
{
System.out.print("\n// +ArgumentList");
}
// ISSUE: guarantee that each instance is visited only once.
for (int i = 0, size = node.items.size(); i < size; i++)
{
Node n = node.items.get(i);
if (n != null)
{
n.evaluate(cx, this);
}
}
if (debug)
{
System.out.print("\n// -ArgumentList");
}
return null;
}
public Value evaluate(Context cx, ListNode node)
{
if (debug)
{
System.out.print("\n// +List");
}
Value val = null;
for (int i = 0, size = node.items.size(); i < size; i++)
{
Node n = node.items.get(i);
val = n.evaluate(cx,this);
node.values.push_back(val);
}
if (debug)
{
System.out.print("\n// -List");
}
return val;
}
/*
* Statements
*/
public Value evaluate(Context cx, StatementListNode node)
{
if (debug)
{
System.out.print("\n// +StatementListNode");
}
NodeFactory nodeFactory = cx.getNodeFactory();
if (node.has_pragma) {
NumberUsage blockUsage = new NumberUsage(number_usage_stack.back());
number_usage_stack.push_back(blockUsage);
}
ObjectValue obj = cx.scope();
boolean inside_class = false;
if (obj.builder instanceof InstanceBuilder ||
obj.builder instanceof ClassBuilder)
{
inside_class = true;
}
boolean inside_cinit = ( this_contexts.last() == cinit_this );
ObjectList<DefinitionNode> hoisted_defs = new ObjectList<DefinitionNode>();
ObjectList<Node> instance_inits = instanceinits_sets.back();
{
// iterate through the statements backwards to find the end point dominator
for (int i = node.items.size() - 1; i >= 0; i--)
{
Node n = node.items.get(i);
if (n != null)
{
// While we are iterating backwards set aside any definitions we find,
// to be processed before the other statements. Handle the initializers
// according to their type (e.g. functions get moved to the start of
// statements, var initializers stay put
// cn: a definition might live in a labelled node
LabeledStatementNode label = null;
if (n instanceof LabeledStatementNode)
{
label = (LabeledStatementNode)n;
n = label.statement;
}
if (n.isDefinition())
{
DefinitionNode def = ((n instanceof DefinitionNode) ? (DefinitionNode)n : null);
// Eval the definition. At this point we are iterating backward
// (to find the end point dominator) so definitions are ordered
// last to first. This is weird, but should not be a problem
// since their initializers are evaluated in the right order.
if(def instanceof IncludeDirectiveNode || def instanceof ImportDirectiveNode)
{
// TODO: Remove eventually
/* SPECIAL CASE (for bug 124494)
* So all statements in an included file have IncludeDirectives
* wrapped around them (e.g. evaluated with the correct [sub-]context):
*
* Hoist a copy of the ID node, leave the original ID node where it is.
* Both hoisted definitions and left behind (non-def) statements
* are correctly bracketed.
*
* This is assuming that, semantically, we want non-defs in an
* included file (the include statement being within a classdef)
* to work, and not throw an error. */
hoisted_defs.push_back(def);
// node.items.set(i, nodeFactory.emptyStatement(node.pos())); // don't do this
}
else if( n.isConst() || inside_class )
{
// If definition is const, then put it and its initializer
// at the beginning of the block and replace current item
// with an empty statement.
hoisted_defs.push_back(def); // C B A
// hoisted_defs.insert(hoisted_defs.begin(),def);
if (label == null)
node.items.set(i, nodeFactory.emptyStatement());
else
label.statement = nodeFactory.emptyStatement();
}
else
{
// Otherwise, leave the initializer at the location of the
// original definition and initalize the variable to its default
// value at the beginning of the block.
hoisted_defs.push_back(def);
if( def.attrs != null && def.attrs.hasAttribute(INTRINSIC) )
{
if (label == null)
node.items.set(i, nodeFactory.emptyStatement());
else
label.statement = nodeFactory.emptyStatement();
}
else
{
Node init = n.initializerStatement(cx);
if (init != null)
{
// Put initializer(if there is one) at position
// of original definition.
if (label == null)
node.items.set(i, init);
else
label.statement = init;
}
else
{
if (label == null)
node.items.set(i, nodeFactory.emptyStatement());
else
label.statement = nodeFactory.emptyStatement();
}
}
}
}
else if (!endpoint_dominator_is_set &&
loop_index == 0 && n.isExpressionStatement() &&
cx.getScopes().size() == 1 )
{
// The end point dominator is the statement that leaves
// the continuation value on the stack. All other results,
// don't need to be pushed onto the stack.
endpoint_dominator = n;
endpoint_dominator_is_set = true;
}
}
}
}
ObjectList<Node> inits = new ObjectList<Node>();
{
// Insert initializers for constants and functions at the
// beginning of the block.
for (int i = hoisted_defs.size() - 1; i >=0; i--) // A B C
{
DefinitionNode def = hoisted_defs.get(i);
{
// pre-process namespace definitions and attributes
boolean is_static = false;
boolean is_intrinsic = false;
boolean is_include = def instanceof IncludeDirectiveNode;
boolean is_namespace = def instanceof NamespaceDefinitionNode;
boolean is_use = def instanceof UseDirectiveNode || def instanceof ImportDirectiveNode;
boolean is_const = false; // tbd
if ( is_namespace || is_const || is_use || is_include )
{
if (def.attrs != null)
{
def.attrs.evaluate(cx, this);
is_static = def.attrs.hasStatic;
is_intrinsic = def.attrs.hasIntrinsic;
}
def.evaluate(cx,this);
}
else
if( def.attrs != null)
{
def.attrs.evaluate(cx,this);
is_static = def.attrs.hasStatic;
is_intrinsic = def.attrs.hasIntrinsic;
}
boolean needs_init = (!is_use && !is_namespace && !is_intrinsic && def.isConst()) || inside_class;
if( inside_cinit && !is_static )
{
instance_inits.push_back(def);
if( needs_init )
{
instance_inits.push_back(def.initializerStatement(cx));
}
}
else
{
if( !is_use && !(def instanceof ClassDefinitionNode && needs_init) ) // ISSUE: remove this special case check
{
inits.push_back(def);
}
if( needs_init )
{
inits.push_back(def.initializerStatement(cx)); // A B C
} // otherwise, there is already a initializer at the orginal point of definition
}
}
}
}
{ // add the non-static inits to the beginning of the statements list
for (int i = inits.size() - 1; i>= 0;i--) // C B A
{
node.items.add(0, inits.get(i)); // A B C
}
}
{
// Now rip through the statements
for (Node n : node.items)
{
if (n == endpoint_dominator)
{
// This statement is the end point dominator, therefore
// set the define_cv flag so the evaluator will know that
// this is an implicit assignment to _cv and will create
// a new definition for it.
define_cv = true;
}
else if (!endpoint_dominator_is_set)
{
// This happens when there is no statement that leaves a
// continuation value on the stack. In this case, an
// empty value is pushed onto the stack at start of the
// program.
define_cv = true;
endpoint_dominator_is_set = true;
}
else
{
// This statement is not the end point dominator, and
// The end point dominator has been set, then do nothing
}
if (n != null)
{
if (!doingMethod() && !n.isDefinition())
{
// We are done with definitions, which means we are doing
// program statements.
StartMethod(fun_name_stack.last(), max_params_stack.last(),
max_locals_stack.last(), max_temps_stack.last(),
false, 0);
}
n.evaluate(cx, this);
}
}
if (!doingMethod() ) // If still not doing method, then start doing it now.
// This happens when you have a block with only definitions
{
StartMethod(fun_name_stack.last(), max_params_stack.last(),
max_locals_stack.last(), max_temps_stack.last(), false, 0);
// Now we are doing a method
}
}
/*
if( node.is_block )
{
usednamespaces_sets.back().pop_back();
default_namespaces.pop_back();
}
*/
if (node.has_pragma) {
node.numberUsage = number_usage_stack.back();
number_usage_stack.pop_back();
}
if (debug)
{
System.out.print("\n// -StatementListNode");
}
return null;
}
public Value evaluate(Context cx, EmptyStatementNode node)
{
if (debug)
{
System.out.print("\n// +EmptyStatementNode");
}
if (debug)
{
System.out.print("\n// -EmptyStatementNode");
}
return null;
}
public Value evaluate(Context cx, ExpressionStatementNode node)
{
if (debug)
{
System.out.print("\n// +ExpressionStatementNode");
}
if (node.expr != null)
{
node.expr.evaluate(cx, this);
}
if (define_cv && !node.isVarStatement()) // Var statements have empty cv's
{
node.gen_bits = getEmitter().NewDef(node);
cx.globalScope().getSlot(cx, SLOT_Global__cv).addDefBits(node.gen_bits);
}
else
if( node.expr != null )
{
node.voidResult();
node.expected_type = cx.voidType();
}
if (debug)
{
System.out.print("\n// -ExpressionStatementNode");
}
return null;
}
public Value evaluate(Context cx, LabeledStatementNode node)
{
if (debug)
{
System.out.print("\n// +LabeledStatementNode");
}
node.label.evaluate(cx, this);
MemberExpressionNode mnode = (MemberExpressionNode) ((ListNode) node.label).items.last();
// grabbing the ref as opposed to evaluating memberexpressionnode to get it (workaround)
ReferenceValue ref = mnode.ref;
ObjectValue obj = cx.getScopes().last();
// Define the label name in the current scope. Use the lableNamespace to avoid collisions
// with other names. loop_index is incremented upon entry to a loop or switch statement,
// and decremented upon exit.
String labelName = null;
if (ref == null)
{
cx.error(node.pos(), kError_InvalidLabel);
}
else
{
labelName = ref.name;
}
if (labelName != null)
{
if (obj.defineName(cx, GET_TOKEN, labelName, ObjectValue.labelNamespace, loop_index) == 0)
{
cx.error(node.pos(), kError_DuplicateLabel);
}
// and if it is a loop label, add a name for the loop label namespace too
if (node.is_loop_label && obj.defineName(cx, GET_TOKEN, labelName, ObjectValue.loopLabelNamespace, loop_index) == 0)
{
cx.error(node.pos(), kError_DuplicateLabel);
}
}
// Evaluate the nested statements. Any references to the label name from a break
// or continue will be replace with that label's loop index.
if (node.statement != null)
{
node.loop_index = loop_index;
// If it's a loop label then share a loop index with the loop statement
// Otherwise give this label a distinct index so that breaks inside of blocks
// end up at the correct target (ie the end of the block)
if( !node.is_loop_label )
{
loop_index++;
LabelStatementBegin();
}
node.statement.evaluate(cx, this);
// If the label is a loop label, then let the loop handle the patchbreak/continue. Since the label and
// the loop share the same loop index this will work out fine (and actually creates problems if we try
// and do it in both places).
if( !node.is_loop_label )
{
LabelStatementEnd(node.loop_index); // patch breaks within labelled statement
loop_index--;
}
}
// Remove the label name now that we are done with it.
if (labelName != null)
{
obj.removeName(cx, GET_TOKEN, labelName, ObjectValue.labelNamespace);
}
if (debug)
{
System.out.print("\n// -LabeledStatementNode");
}
return null;
}
public Value evaluate(Context cx, IfStatementNode node)
{
if (debug)
{
System.out.print("\n// +IfStatementNode");
}
Value val = node.condition.evaluate(cx, this);
val = val!=null?val.getValue(cx):null;
if( val != null && val.getType(cx).getTypeValue() == cx.booleanType() ) // If there is a boolean value, use it to compile out code
{
node.is_true = val.booleanValue()?true:false;
node.is_false = val.booleanValue()?false:true;
if( node.is_true )
{
if( node.thenactions != null )
{
node.thenactions.evaluate(cx,this);
}
}
else
if( node.is_false ) // always the case, in this case
{
if( node.elseactions != null)
{
node.elseactions.evaluate(cx,this);
}
}
}
else
{
If(IF_false); // if false, jump past then actions
if (node.thenactions != null)
{
node.thenactions.evaluate(cx, this);
}
Else(); // jump past else actions
PatchIf(getIP()); // patch target of if jump
if (node.elseactions != null)
{
node.elseactions.evaluate(cx, this);
}
PatchElse(getIP()); // patch target of else jump
}
if (debug)
{
System.out.print("\n// -IfStatementNode");
}
return null;
}
int inside_switch = 0;
public Value evaluate(Context cx, SwitchStatementNode node)
{
if (debug)
{
System.out.print("\n// +SwitchStatementNode");
}
int t = allocateTemp(); // approximation of actual temp usage.
case_exprs.add(new CaseList());
SwitchBegin(); // jump past statements
if (node.statements != null)
{
++inside_switch;
node.loop_index = loop_index++;
node.statements.evaluate(cx, this);
--loop_index;
--inside_switch;
}
// See if there is a default case. If not, then add one.
{
CaseList case_expr = case_exprs.last();
if (!case_expr.hasDefault)
{
// no default label, lets add one.
case_expr.add(null);
CaseLabel(true); // default, just in case there wasn't one yet.
Break(node.loop_index); // default loop_index
}
}
Break(node.loop_index); // Last chance break, in case there isn't one
PatchSwitchBegin(getIP()); // patches initial jump past statements
node.expr.evaluate(cx, this); // leaves value of governing expr on stack
{
int case_index = 0;
int default_index = 0;
ObjectList<Node> case_expr = case_exprs.removeLast();
int case_expr_size = case_expr.size();
if (case_expr_size != 0)
{
for (case_index = 0; case_index < case_expr_size; ++case_index)
{
Node expr = case_expr.get(case_index);
if (expr != null)
{ // skip default ( expr == 0 )
/* do operands */
expr.evaluate(cx, this);
}
else
{
default_index = case_index;
}
If(IF_false); // if false, jump past then actions
PushCaseIndex(case_index);
Else(); // jump past else actions
PatchIf(getIP()); // patch target of if jump
}
}
PushCaseIndex(default_index);
while (case_index-- != 0)
{
PatchElse(getIP()); // patch target of else jumps
/* this is like nested ifs:
* if(...) 0;
* else if(...) 1;
* else if(...) 2;
* else if(...) 3;
* else 4; // default;
* you keep trying conditions until you find one
* that is true and then evaluate the statement
* and jump to the end.
*/
}
}
SwitchTable(); // jumps to addr for case index
PatchContinue(node.loop_index);
PatchBreak(node.loop_index); // patches jump past switch table
// Even though there are no continues in switch statements,
// do this to pop the empty continue_addrs vector.
freeTemp(t);
if (debug)
{
System.out.print("\n// -SwitchStatementNode");
}
return null;
}
public Value evaluate(Context cx, CaseLabelNode node)
{
if (debug)
{
System.out.print("\n// +CaseLabelNode");
}
CaseList caseList = case_exprs.last();
caseList.add(node.label);
if (node.label == null)
{
// this is a default label
if (caseList.hasDefault)
{
cx.error(node.pos(), kError_MultipleSwitchDefaults);
}
else
{
caseList.hasDefault = true;
}
}
CaseLabel(node.label == null); // indicate if is default
// reset the block of this node so that it is in the same
// block as the statements which it corresponds to. The
// previous block might be unreachable and we don't want
// to have this label node culled during dead code removal.
node.block = null;
checkFeature(cx, node);
if (debug)
{
System.out.print("\n// -CaseLabelNode");
}
return null;
}
int inside_loop = 0;
public Value evaluate(Context cx, DoStatementNode node)
{
if (debug)
{
System.out.print("\n// +DoStatementNode");
}
LoopBegin();
PatchLoopBegin(getIP());
if (node.statements != null)
{
++inside_loop;
node.loop_index = loop_index++;
node.statements.evaluate(cx, this);
--loop_index;
--inside_loop; // because loop_index inlcudes labeled statements
}
PatchContinue(node.loop_index);
if (node.expr != null)
{
node.expr.evaluate(cx, this);
}
else
{
}
LoopEnd(IF_true);
PatchBreak(node.loop_index);
if (debug)
{
System.out.print("\n// -DoStatementNode");
}
return null;
}
public Value evaluate(Context cx, WhileStatementNode node)
{
if (debug)
{
System.out.print("\n// +WhileStatementNode");
}
LoopBegin();
if (node.statement != null)
{
++inside_loop;
node.loop_index = loop_index++;
node.statement.evaluate(cx, this);
--loop_index;
--inside_loop;
}
PatchLoopBegin(getIP());
PatchContinue(node.loop_index);
if (node.expr != null)
{
node.expr.evaluate(cx, this);
}
else
{
}
LoopEnd(IF_true);
PatchBreak(node.loop_index);
if (debug)
{
System.out.print("\n// -WhileStatementNode");
}
return null;
}
public Value evaluate(Context cx, ForStatementNode node)
{
if (debug)
{
System.out.print("\n// +ForStatementNode");
}
if (node.initialize != null)
{
if (node.initialize.isDefinition())
{
ExpressionStatementNode es = (ExpressionStatementNode) node.initialize.initializerStatement(cx);
node.initialize = es.expr;
}
node.initialize.evaluate(cx, this);
}
LoopBegin();
if (node.statement != null)
{
++inside_loop;
node.loop_index = loop_index++;
node.statement.evaluate(cx, this);
--loop_index;
--inside_loop;
}
PatchContinue(node.loop_index);
if (node.increment != null)
{
node.increment.evaluate(cx, this);
}
PatchLoopBegin(getIP());
if (node.test != null)
{
node.test.evaluate(cx, this);
}
else
{
}
LoopEnd(IF_true);
PatchBreak(node.loop_index);
if (debug)
{
System.out.print("\n// -ForStatementNode");
}
return null;
}
public Value evaluate(Context cx, WithStatementNode node)
{
if (debug)
{
System.out.print("\n// +WithStatementNode");
}
if (node.expr != null)
{
node.expr.evaluate(cx, this);
}
PushScope();
node.activation = new ObjectValue(cx, new WithBuilder(), null);
cx.pushScope(node.activation);
if (!with_used_stack.isEmpty())
{
with_used_stack.removeLast();
with_used_stack.add(1);
}
int saveWithDepth = cx.statics.withDepth;
cx.statics.withDepth = cx.getScopes().size()-1;
if (node.statement != null)
{
node.statement.evaluate(cx, this);
}
cx.statics.withDepth = saveWithDepth;
PopScope();
cx.popScope();
if (debug)
{
System.out.print("\n// -WithStatementNode");
}
return null;
}
public Value evaluate(Context cx, ContinueStatementNode node)
{
if (debug)
{
System.out.print("\n// +ContinueStatementNode");
}
if (node.id != null)
{
Value val = node.id.evaluate(cx, this);
ReferenceValue ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
if(ref != null)
{
ref.setQualifier(cx, ObjectValue.loopLabelNamespace);
ref.setBase(cx.getScopes().last());
ref.setKind(GET_TOKEN);
ref.getSlot(cx, GET_TOKEN); // does the lookup
node.loop_index = ref.getSlotIndex(GET_TOKEN);
}
if( inside_loop < 0 )
{
// it is an error
node.loop_index = -1;
}
}
else
{
if( inside_loop > 0 )
{
// target is outside of current loop
node.loop_index = loop_index - 1;
}
else
{
// otherwise, it is an error
node.loop_index = -1;
}
}
if( node.loop_index < 0 )
{
cx.error(node.pos(), kError_ContinueHasNoTarget);
}
Continue(node.loop_index);
if (debug)
{
System.out.print("\n// -ContinueStatementNode");
}
return null;
}
public Value evaluate(Context cx, BreakStatementNode node)
{
if (debug)
{
System.out.print("\n// +BreakStatementNode");
}
if (node.id != null)
{
Value val = node.id.evaluate(cx, this);
ReferenceValue ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
if(ref != null) {
ref.setQualifier(cx, ObjectValue.labelNamespace);
ref.setBase(cx.getScopes().last());
ref.setKind(GET_TOKEN);
ref.getSlot(cx, GET_TOKEN); // does the lookup
node.loop_index = ref.getSlotIndex(GET_TOKEN);
}
}
else
{
if( inside_loop > 0 || inside_switch > 0 )
{
node.loop_index = loop_index - 1;
// subtract 1 since we are now inside the loop.
}
else
{
node.loop_index = -1; // break without expression must have loop target
}
}
//TODO improve this error to suggest {} blocks
if( node.loop_index < 0 )
{
cx.error(node.pos(), kError_BreakHasNoTarget);
}
Break(node.loop_index);
if (debug)
{
System.out.print("\n// -BreakStatementNode");
}
return null;
}
public Value evaluate(Context cx, ReturnStatementNode node)
{
if (debug)
{
System.out.print("\n// +ReturnStatementNode");
}
if (this_contexts.last() == cinit_this)
{
cx.error(node.pos(), kError_CannotReturnFromStaticInit);
}
else if (this_contexts.last() == package_this)
{
cx.error(node.pos(), kError_CannotReturnFromPackageInit);
}
else if (this_contexts.last() == global_this && cx.scope().builder instanceof GlobalBuilder)
{
cx.error(node.pos(), kError_CannotReturnFromGlobalInit);
}
if (node.expr != null)
{
node.expr.evaluate(cx, this);
}
else
{
}
Return(TYPE_none);
if( super_context.last() == super_statement )
{
super_context.set(super_context.size()-1, super_error2);
}
if (debug)
{
System.out.print("\n// -ReturnStatementNode");
}
return null;
}
public Value evaluate(Context cx, ThrowStatementNode node)
{
if (debug)
{
System.out.print("\n// +ThrowStatementNode");
}
if (node.expr != null)
{
node.expr.evaluate(cx, this);
}
// cn: A synthetic "throw x" is inserted into catch blocks
// by NodeFactory in order to make finally blocks work. Don't call
// FlowGraphEmitter's Throw() in that case because we aren't really
// exiting the block. Basically a bandaid over a hack
if (node.isSynthetic() == false)
Throw();
if( super_context.last() == super_statement )
{
super_context.set(super_context.size()-1, super_error2);
}
if (debug)
{
System.out.print("\n// -ThrowStatementNode");
}
return null;
}
public Value evaluate(Context cx, TryStatementNode node)
{
if (debug)
{
System.out.print("\n// +TryStatementNode");
}
// Evaluate the try {} block
Try(node.finallyblock != null);
if( node.tryblock != null )
{
node.tryblock.evaluate(cx, this);
}
// Generate the catch blocks
CatchClausesBegin();
if( node.catchlist != null )
{
node.catchlist.evaluate(cx, this);
}
CatchClausesEnd();
if( node.finallyblock != null )
{
node.finallyblock.evaluate(cx,this);
}
FinallyClauseEnd();
if (debug)
{
System.out.print("\n// -TryStatementNode");
}
return null;
}
public Value evaluate(Context cx, CatchClauseNode node)
{
if (debug)
{
System.out.print("\n// +CatchClauseNode");
}
CatchBuilder catchBuilder = new CatchBuilder();
node.activation = new ObjectValue(cx, catchBuilder,cx.noType());
cx.pushScope(node.activation);
if (node.parameter != null)
{
catchBuilder.hasParameter = true;
node.parameter.evaluate(cx, this);
ParameterNode parameter = (ParameterNode)node.parameter;
if (parameter.typeref != null) // parameter->type was evaluated when we called parameter->evaluate()
{
node.typeref = parameter.typeref;
}
if (parameter.type != null)
{
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
Value val = parameter.type.evaluate(cx,this);
node.typeref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
if( node.typeref != null )
{
node.typeref.setTypeAnnotation(true);
}
ObjectValue obj = cx.scope();
if (obj.builder instanceof ActivationBuilder)
{
body_unresolved_sets.last().addAll(unresolved);
}
else
{
ce_unresolved_sets.last().addAll(unresolved);
}
unresolved.clear();
}
}
Catch(null, null);
// Reset the block of the Catch node
node.block = null;
checkFeature(cx, node);
if (node.statements != null)
{
node.statements.evaluate(cx, this);
exceptions_used_stack.removeLast();
exceptions_used_stack.add(1);
}
cx.popScope();
if (debug)
{
System.out.print("\n// -CatchClauseNode");
}
return null;
}
public Value evaluate(Context cx, FinallyClauseNode node)
{
if (debug)
{
System.out.print("\n// +FinallyClauseNode");
}
node.default_catch.evaluate(cx,this);
if( node.statements != null )
{
node.statements.evaluate(cx,this);
}
if (debug)
{
System.out.print("\n// -FinallyClauseNode");
}
return null;
}
public Value evaluate(Context unused_cx, FunctionCommonNode node)
{
if (debug)
{
System.out.print("\n// +FunctionCommonNode");
}
Context cx = node.cx; // switch to original context
PackageDefinitionNode pkgdef = node.def != null ? node.def.pkgdef : null;
if( pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.push_back(pkgdef.publicNamespace);
default_namespaces.push_back(pkgdef.internalNamespace);
usednamespaces_sets.push_back(pkgdef.used_namespaces);
used_def_namespaces_sets.push_back(pkgdef.used_def_namespaces);
importednames_sets.push_back(pkgdef.imported_names);
}
int savedWithDepth = cx.statics.withDepth;
if( node.with_depth != -1)
{
// FCN was hoisted by an earlier pass
cx.statics.withDepth = node.with_depth;
}
ObjectList<ObjectValue>saved_scopes = null;
if( node.scope_chain != null )
{
saved_scopes = cx.swapScopeChain(node.scope_chain);
}
// First time through, initialize the compile-time constant function value.
if (node.fun == null)
{
if(node.used_namespaces == null) node.used_namespaces = new Namespaces(usednamespaces_sets.back().size());
node.used_namespaces.addAll(usednamespaces_sets.back()); // makes a copy
if(node.imported_names == null) node.imported_names = new Multinames();
node.imported_names.putAll(importednames_sets.back()); // makes a copy
node.private_namespace = (private_namespaces.size() != 0) ? private_namespaces.back() : null;
node.default_namespace = default_namespaces.back();
node.public_namespace = cx.publicNamespace(); //public_namespaces.back();
node.fun = new ObjectValue(cx,new FunctionBuilder(),cx.functionType());
boolean is_named_anon = false;
if( !node.isFunctionDefinition() && node.identifier != null && !"anonymous".equals(node.identifier.name) && node.isUserDefinedBody() )
{
is_named_anon = true;
node.setNamedInnerFunc(true);
// Create a slot in the FunctionBuilder to represent this function so that it can recursively
// call itself.
Namespaces temp_ns = new Namespaces(cx.publicNamespace());
int method_id = node.fun.builder.Method(cx,node.fun,node.identifier.name,temp_ns,false);
node.fun.builder.ExplicitCall(cx,node.fun,node.identifier.name,temp_ns,cx.noType(),false,false,-1,method_id,-1);
cx.pushScope(node.fun);
}
// Save the with depth, if there is one, since the FunctionCommonNode is going to get hoisted
// and there won't be a WithStatementNode above it anymore after hoisting.
if( cx.statics.withDepth != -1 )
node.with_depth = cx.statics.withDepth;
ObjectList<ObjectValue> scope_chain = cx.getScopes();
for(int s = scope_chain.size(); s > 0; --s )
{
ObjectValue scope = scope_chain.get(s-1);
if( scope.builder instanceof CatchBuilder ||
scope.builder instanceof WithBuilder ||
scope.builder instanceof ActivationBuilder)
{
node.scope_chain = new ObjectList<ObjectValue>(scope_chain); // copy the scope chain, since the func will be hoisted
break;
}
}
if( is_named_anon )
{
// We can pop the scope now since it's been saved by the function.
cx.popScope();
}
// fexpr_sets is a stack of sets of function expressions.
// Each set contains the functions at a particular scope level.
ObjectValue scope = cx.scope();
Builder b = scope.builder;
if (b instanceof ClassBuilder)
{
int size = staticfexprs_sets.last().size();
int i;
// Look for the current node in the set for the current scope.
for (i = 0; i < size && staticfexprs_sets.last().get(i) != node; ++i);
// If it is not in the set, then add it.
if (i >= size)
{
staticfexprs_sets.last().add(node);
}
}
else
{
int size = fexprs_sets.last().size();
int i;
// Look for the current node in the set for the current scope.
for (i = 0; i < size && fexprs_sets.last().get(i) != node; ++i);
// If it is not in the set, then add it.
if (i >= size)
{
fexprs_sets.last().add(node);
}
}
}
else
if( doingMethod() )
{
}
else
if (node.ref == null)
{
if(node.used_namespaces != null)
usednamespaces_sets.push_back(node.used_namespaces);
if(node.imported_names != null)
importednames_sets.push_back(node.imported_names);
// Start processing a new function. Add an empty function set to the
// function sets.
fexprs_sets.add(new ObjectList<FunctionCommonNode>());
staticfexprs_sets.push_back(new ObjectList<FunctionCommonNode>());
instanceinits_sets.push_back(new ObjectList<Node>());
// Create a reference to the name.
Value val = node.identifier.evaluate(cx, this);
node.ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
node.fun.name = node.internal_name; // ISSUE: don't know if this is necessary.
region_name_stack.push_back(cx.debugName(region_name_stack.back(),node.ref.name,node.namespace_ids,node.kind));
// The activation object is the compile-time model of the
// function activation. It is different than the function
// object, which represents the function constructor.
node.fun.activation = new ObjectValue(cx, new ActivationBuilder(),cx.noType());
{
ObjectValue obj = cx.scope(cx.getScopes().size()-1);
node.fun.activation.builder.classname = obj.builder.classname;
node.fun.activation.name = node.internal_name;
}
ObjectValue fun = node.fun;
cx.pushScope(fun.activation);
// The following stacks are not actually used. They are
// left here for their side effects.
// ISSUE: remove references to these stacks.
fun_name_stack.add(node.internal_name);
max_params_stack.add(node.signature.size());
max_locals_stack.add(node.body != null ? node.var_count : 0);
max_temps_stack.add(node.body != null ? node.temp_count : 0);
with_used_stack.add(0);
exceptions_used_stack.add(0);
if( node.use_stmts != null )
{
node.use_stmts.evaluate(cx,this);
}
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
node.signature.evaluate(cx, this);
ce_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
int scope_index = cx.getScopes().size()-1;
ObjectValue obj = cx.scope(scope_index);
if( (node.needsArguments&METHOD_Arguments) != 0 )
{
ObjectValue namespace = node.default_namespace;
if( !obj.hasName(cx,GET_TOKEN,"arguments",namespace) )
{
Builder bui = obj.builder;
int var_id = bui.Variable(cx,obj);
bui.ExplicitVar(cx,obj,"arguments",namespace,cx.arrayType(),-1,-1,var_id);
}
else
{
node.needsArguments ^= METHOD_Arguments; // don't actually need 'arguments' since there is a parameter or var with the same name
}
}
boolean is_constructor = "$construct".equals(node.ref.name);
if (is_constructor)
{
if (node.signature.result != null)
{
cx.error(node.pos(), kError_CtorWithReturnType);
}
}
if (node.body != null)
{
if( !node.isFunctionDefinition() )
{
this_contexts.add(global_this);
}
int state = super_error;
if (is_constructor)
{
state = super_statement;
}
super_context.add(state);
if (node.signature.inits != null)
{
int scope_depth = cx.getScopeDepth();
ObjectValue iframe = cx.scope(scope_depth-2);
// Make get & method slots invisible, only set slots will be visible.
if( iframe.builder instanceof InstanceBuilder )
iframe.setInitOnly(true);
this_contexts.push_back(init_this);
node.signature.inits.evaluate(cx, this);
this_contexts.pop_back();
if( iframe.builder instanceof InstanceBuilder )
iframe.setInitOnly(false);
}
if (is_constructor && cx.dialect(11))
{
super_context.set(super_context.size()-1, super_error_es4);
}
node.body.evaluate(cx, this);
node.temp_count = getTempCount();
node.var_count = fun.activation.var_count-node.signature.size();
super_context.pop_back();
if( !node.isFunctionDefinition() )
{
this_contexts.pop_back();
}
}
else
{
StartMethod(fun_name_stack.last(), max_params_stack.last(), 0 /* no locals */);
}
TypeInfo type = null;
ObjectList<TypeInfo> types = null;
FinishMethod(cx,fun_name_stack.back(),type,types,node.fun.activation,node.needsArguments,cx.getScopes().size(),node.debug_name,node.isNative(),false, null);
if (with_used_stack.last() != 0)
{
node.setWithUsed(true);
}
with_used_stack.removeLast();
if (exceptions_used_stack.last() != 0)
{
node.setExceptionsUsed(true);
}
exceptions_used_stack.removeLast();
fun_name_stack.removeLast();
// Store the accumulated fexprs in this node for CodeGeneration
node.fexprs = fexprs_sets.back();
// Now evaluate each function expression
this_contexts.add(global_this);
for (Node fexpr : node.fexprs)
{
fexpr.evaluate(cx, this);
}
this_contexts.pop_back();
instanceinits_sets.pop_back();
staticfexprs_sets.pop_back();
fexprs_sets.pop_back();
cx.popScope(); // activation
region_name_stack.pop_back();
usednamespaces_sets.pop_back();
importednames_sets.pop_back();
}
// Restore the scope chain if it was changed
if( saved_scopes != null )
{
cx.swapScopeChain(saved_scopes);
}
// Reset the withDepth to whatever it was
cx.statics.withDepth = savedWithDepth;
if( pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.pop_back();
default_namespaces.pop_back();
usednamespaces_sets.pop_back();
used_def_namespaces_sets.pop_back();
importednames_sets.pop_back();
}
if (debug)
{
System.out.print("\n// -FunctionCommonNode");
}
return node.fun;
}
public Value evaluate(Context unused_cx, BinaryProgramNode node)
{
if( node.frame != null )
{
// We are an import from an .abc file. Need to fill in any missing type info
Iterator<ClassDefinitionNode> it = node.clsdefs.iterator();
// Need to do this so that the slots from this import get put into the real
// import frame.
node.cx.scope().builder.addNames(node.frame.builder.getNames());
inheritSlots(node.frame, node.cx.scope(), node.cx.scope().builder, node.cx);
node.frame.builder.clearNames();
while( it.hasNext() )
{
// Add the classdef's from the abc file to the clsdefs list.
// these will be sorted, and then evaluated in the correct order later.
ClassDefinitionNode cdn = it.next();
clsdefs_sets.last().add(cdn);
}
}
// TODO: better dependency analysis for ABC files.
// this is filled in during AbcParse, and then added here - we could be doing this dependency analysis earlier
ce_unresolved_sets.last().addAll(node.ce_unresolved);
return null;
}
private void processImports(Context cx, ProgramNode node, ObjectList<ImportNode> imports)
{
ObjectValue frame = cx.scope();
Builder globalBuilder = new GlobalBuilder();
node.importFrame = new ObjectValue(cx,globalBuilder,null);
NodeFactory nf = cx.getNodeFactory();
ImportNode imported_program_nodes = null;
for (ImportNode it : imports)
{
if( imported_program_nodes == null )
{
imported_program_nodes = nf.Import(cx,nf.literalString("",0),it.program);
}
else
{
imported_program_nodes.program.statements.items.addAll(it.program.statements.items);
imported_program_nodes.program.pkgdefs.addAll(it.program.pkgdefs);
}
}
if( imported_program_nodes != null )
{
cx.popScope();
cx.pushScope(node.importFrame);
// 1. ProgramNode.state == Inheritance
import_context.push_back(true);
imported_program_nodes.program.evaluate(cx,this);
// 2. ProgramNode.state == else
imported_program_nodes.program.evaluate(cx,this);
import_context.pop_back();
cx.popScope();
cx.pushScope(frame);
imported_program_nodes.evaluate(cx,this);
}
}
public Value evaluate(Context unused_cx, ProgramNode node)
{
if (debug)
{
System.out.print("\n// +ProgramNode");
}
if (node.state == ProgramNode.Inheritance)
{
if( node.frame != null )
{
// already done
return null;
}
Context cx = node.cx;
programNode_temp = allocateTemp();
strict_context.push_back(false);
node.frame = cx.scope();
processImports(cx, node, node.imports);
// reset endpoint_dominator/define_cv flags which were set during import processing.
define_cv = false;
endpoint_dominator_is_set = false;
StartProgram("");
this_contexts.add(global_this);
super_context.add(super_error);
number_usage_stack.add(new NumberUsage()); // place to hang numeric usage info
// Function expressions that occur in the current block will be
// compiled as though they had occured at the end of the block.
// The variable that references them is initialized at the beginning
// of the block.
fexprs_sets.add(new ObjectList<FunctionCommonNode>());
clsdefs_sets.add(new ObjectList<ClassDefinitionNode>());
staticfexprs_sets.add(new ObjectList<FunctionCommonNode>());
instanceinits_sets.add(new ObjectList<Node>());
package_unresolved_sets.push_back(node.package_unresolved);
import_def_unresolved_sets.push_back(node.import_def_unresolved);
ns_unresolved_sets.push_back(node.ns_unresolved);
fa_unresolved_sets.push_back(node.fa_unresolved);
ce_unresolved_sets.push_back(node.ce_unresolved);
body_unresolved_sets.push_back(node.body_unresolved);
rt_unresolved_sets.push_back(node.rt_unresolved);
// Copy the set of nested functions into the node for use
// by later phases.
node.fexprs = fexprs_sets.last();
node.clsdefs = clsdefs_sets.last();
fun_name_stack.add("$init");
max_params_stack.add(0);
max_locals_stack.add(node.var_count); // Should always be zero.
max_temps_stack.add(node.temp_count);
node.var_count = 0; // no explicit locals in global scope
node.public_namespace = cx.publicNamespace();
this.local_file_namespace = node.default_namespace = cx.getNamespace(cx.getFileInternalNamespaceName(),Context.NS_PRIVATE);
public_namespaces.push_back(node.public_namespace);
default_namespaces.push_back(node.default_namespace);
usednamespaces_sets.push_back(node.used_def_namespaces);
used_def_namespaces_sets.push_back(new Namespaces());
importednames_sets.push_back(new Multinames());
usednamespaces_sets.back().push_back(node.public_namespace);
usednamespaces_sets.back().push_back(node.default_namespace);
used_def_namespaces_sets.back().push_back(node.public_namespace);
used_def_namespaces_sets.back().push_back(node.default_namespace);
region_name_stack.push_back("");
if (node.statements != null)
{
for(PackageDefinitionNode it : node.pkgdefs)
{
it.evaluate(cx,this);
}
node.statements.evaluate(cx, this);
define_cv = false; // this turns off further processing of cv
}
node.temp_count = getTempCount(); // Remember the temp count
Return(TYPE_none);
FinishMethod(cx,"$init",null,null,null,0,1,"",false,false, null);
// By now, the in and out sets have been computed.
// Get the use definitions for the end of the program,
// and mark them as value needed.
ObjectList<Node> defs = getEmitter().GetDefs(getEmitter().getBlock().out_bits);
{
for (Node n : defs)
{
n.expectedType(cx.noType());
}
}
// Now evaluate each function expression
{
for (FunctionCommonNode n : node.fexprs)
{
n.evaluate(cx, this);
}
}
// Remove the top set of nested functions from the stack of sets
fexprs_sets.removeLast();
// ASSERT(fexprs_sets.size() == 0);
// Now evaluate each class expression
{
// set resolveInheritance to true so that when processing ClassDefinitionNode, FA only tries to
// resolve node.baseclass...
resolveInheritance = true;
for (ClassDefinitionNode n : node.clsdefs)
{
n.evaluate(cx,this);
}
resolveInheritance = false;
}
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
node.state = ProgramNode.Else;
}
else
{
Context cx = node.cx;
{
// node.clsdefs have the baseclass.cframe resolved, i.e. we've got fully-qualified class names.
// sort the class names based on "extends" and "implements"...
if (found_circular_or_duplicate_class_definition == false)
{
node.clsdefs = sortClassDefinitions(node.cx, node.clsdefs);
}
// now that node.clsdefs are in topological order, check for overrides and other steps that
// require resolved supertypes should be more accurate...
if (found_circular_or_duplicate_class_definition == false)
{
for (ClassDefinitionNode n : node.clsdefs)
{
n.evaluate(cx,this);
}
}
}
// Remove the top set of nested classes from the stack of sets
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
package_unresolved_sets.pop_back();
import_def_unresolved_sets.pop_back();
ns_unresolved_sets.pop_back();
fa_unresolved_sets.pop_back();
ce_unresolved_sets.pop_back();
body_unresolved_sets.pop_back();
rt_unresolved_sets.pop_back();
clsdefs_sets.removeLast();
instanceinits_sets.removeLast();
staticfexprs_sets.removeLast();
region_name_stack.removeLast();
super_context.removeLast();
this_contexts.removeLast();
strict_context.removeLast();
importednames_sets.pop_back();
usednamespaces_sets.removeLast();
used_def_namespaces_sets.removeLast();
default_namespaces.removeLast();
public_namespaces.removeLast();
// ASSERT(fexprs_sets.size() == 0);
FinishProgram(cx,"",0);
freeTemp(programNode_temp);
node.state = ProgramNode.Done;
} // if
if (debug)
{
System.out.print("\n// -ProgramNode");
}
return null;
}
// 1. setup a dependency graph based on "extends" and "implements".
// 2. run topological sort to determine the export order.
// 3. output error if part of the dependency graph forms cycles.
private ObjectList<ClassDefinitionNode> sortClassDefinitions(Context cx, final ObjectList<ClassDefinitionNode> clsdefs)
{
// skip sorting if there are less than 2 classes...actually, don't skip the case
// of one class definition in case it is self referential
if (clsdefs == null || clsdefs.size() == 0)
{
return clsdefs;
}
// create a dependency graph, the weight is ClassDefinitionNode...
final DependencyGraph<ClassDefinitionNode> g = new DependencyGraph<ClassDefinitionNode>();
for (ClassDefinitionNode clsdef : clsdefs)
{
// The dependency graph doubles as a hashtable
String className = clsdef.cframe.builder.classname.toString();
g.put(className, clsdef);
// if the class is not already in the graph as a node, add it.
if (!g.containsVertex(className))
{
g.addVertex(new Vertex<String>(className));
}
// add dependency... add two vertices and an edge.
if (clsdef.cframe.baseclass != null && !"Class".equals(clsdef.cframe.baseclass.name.toString()))
{
g.addDependency(className, clsdef.cframe.baseclass.name.toString());
}
// do the same things to interfaces...
int size = (clsdef.interfaces == null) ? 0 : clsdef.interfaces.values.size();
for (int i = 0; i < size; i++)
{
Value val = clsdef.interfaces.values.get(i);
if( val instanceof ReferenceValue )
{
ReferenceValue ref = (ReferenceValue) val;
if (ref != null)
{
Value v2 = ref.getValue(cx);
TypeValue t = ((v2 instanceof TypeValue) ? (TypeValue)v2 : null);
if (t != null)
{
g.addDependency(className, t.builder.classname.toString());
}
}
}
}
}
final ObjectList<ClassDefinitionNode> tsort = new ObjectList<ClassDefinitionNode>();
// sort the classes
Algorithms.topologicalSort(g, new Visitor<String>()
{
public void visit(Vertex<String> v)
{
String name = v.getWeight();
// make sure that the name corresponds to a local ClassDefinitionNode...
if (g.containsKey(name))
{
tsort.add(g.get(name));
}
}
});
// if the sort returns fewer classes, that means some nodes form cycle(s)...
if (clsdefs.size() > tsort.size())
{
for (ClassDefinitionNode clsdef : clsdefs)
{
// output errors against the nodes in dependency cycles...
if (!tsort.contains(clsdef))
{
cx.error(clsdef.pos(), kError_CircularReference, clsdef.cframe.builder.classname.name);
found_circular_or_duplicate_class_definition = true;
}
}
return clsdefs;
}
else
{
return tsort;
}
}
/*
Every package definition is its own global scope. Packages can't see the script
global scope, since packages come before any global code and non-package
definitions. Implementations are free to use the scope chain or prototype
chain to inherit the built-in properties
*/
public Value evaluate(Context unused_cx, PackageDefinitionNode node)
{
if (debug)
{
System.out.print("\n// +PackageDefinitionNode");
}
Context cx = node.cx; // switch to original context
if( node.publicNamespace == null )
{
if( doingPackage() && import_context.back() != true )
{
cx.error(node.pos(), kError_NestedPackage);
return null;
}
if( node.ref == null )
{
node.ref = (ReferenceValue)(node.name.evaluate(cx,this));
}
node.publicNamespace = cx.getNamespace(node.ref.name);
node.publicNamespace.setPackage(true);
node.internalNamespace = cx.getNamespace(node.ref.name, Context.NS_INTERNAL);
node.used_namespaces.push_back(local_file_namespace);
node.used_def_namespaces.push_back(local_file_namespace);
node.used_namespaces.push_back(cx.publicNamespace());
node.used_def_namespaces.push_back(cx.publicNamespace());
if( cx.publicNamespace() != node.publicNamespace )
{
node.used_namespaces.push_back(node.publicNamespace);
node.used_def_namespaces.push_back(node.publicNamespace);
}
node.used_namespaces.push_back(node.internalNamespace);
node.used_def_namespaces.push_back(node.internalNamespace);
}
else
if( !node.in_this_pkg )
{
node.in_this_pkg = true;
this_contexts.add(package_this);
super_context.add(super_error);
strict_context.push_back(true);
public_namespaces.push_back(node.publicNamespace);
default_namespaces.push_back(node.internalNamespace);
usednamespaces_sets.push_back(node.used_namespaces);
used_def_namespaces_sets.push_back(node.used_def_namespaces);
importednames_sets.push_back(node.imported_names);
}
else
{
node.in_this_pkg = false;
this_contexts.pop_back();
super_context.pop_back();
strict_context.pop_back();
usednamespaces_sets.pop_back();
used_def_namespaces_sets.pop_back();
importednames_sets.pop_back();
default_namespaces.pop_back();
public_namespaces.pop_back();
}
if (debug)
{
System.out.print("\n// -PackageDefinitionNode");
}
return null;
}
public Value evaluate(Context cx, PackageNameNode node)
{
return node.id.evaluate(cx, this);
}
public Value evaluate(Context cx, PackageIdentifiersNode node)
{
ReferenceValue ref = new ReferenceValue(cx, null, "", cx.publicNamespace()); // caller deletes
// qualifier gets set by caller
ref.setPosition(node.pos());
ref.name = node.pkg_part.intern();
return ref;
}
public Value evaluate(Context cx, Node node)
{
cx.internalError( node.pos(), "Feature not supported: " + node.toString());
return null;
}
public Value evaluate(Context cx, VariableDefinitionNode node)
{
if(node.cx != null) {
cx = node.cx;
}
Value val = null;
// Set up access namespaces
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.push_back(node.pkgdef.publicNamespace);
default_namespaces.push_back(node.pkgdef.internalNamespace);
usednamespaces_sets.push_back(node.pkgdef.used_namespaces);
used_def_namespaces_sets.push_back(node.pkgdef.used_def_namespaces);
importednames_sets.push_back(node.pkgdef.imported_names);
}
//
boolean is_static = false;
boolean is_final = false;
boolean is_dynamic = false;
boolean is_override = false;
if (node.attrs != null)
{
is_static = node.attrs.hasStatic;
is_final = node.attrs.hasFinal;
is_dynamic = node.attrs.hasDynamic;
is_override = node.attrs.hasOverride;
}
ClassBuilder classBuilder = classBuilderOnScopeChain(cx);
if (classBuilder != null && classBuilder.is_interface)
{
cx.error(node.pos(), kError_VarInInterface);
}
if (is_dynamic)
{
cx.error(node.pos(), kError_InvalidDynamic);
}
if (is_final)
{
cx.error(node.pos(), kError_InvalidFinalUsage);
}
if (is_override)
{
cx.error(node.pos(), kError_InvalidOverrideUsage);
}
if( is_static )
{
if (classBuilder != null)
{
val = node.list.evaluate(cx,this);
}
else
{
cx.error(node.attrs.pos(), kError_InvalidStatic);
}
}
else
{
val = node.list.evaluate(cx, this);
}
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.pop_back();
default_namespaces.pop_back();
usednamespaces_sets.pop_back();
used_def_namespaces_sets.pop_back();
importednames_sets.pop_back();
}
return val;
}
// This should be called after AttributeListNode* attrs has already been evaluated
public void computeNamespaces( Context cx, AttributeListNode attrs, Namespaces namespaces, ObjectList<String> namespace_ids )
{
if( attrs != null )
{
if( attrs.hasPrivate )
{
if( private_namespaces.size() == 0 )
{
cx.error(attrs.pos(), kError_InvalidPrivate);
attrs.namespaces.push_back(cx.publicNamespace()); // for graceful failure
attrs.namespace_ids.push_back(PRIVATE);
}
else
{
attrs.namespaces.push_back(private_namespaces.back());
attrs.namespace_ids.push_back(PRIVATE);
}
}
else
if( attrs.hasProtected )
{
if (cx.scope().builder instanceof InstanceBuilder)
{
if( protected_namespaces.size() == 0 )
{
cx.error(attrs.pos(), kError_InvalidProtected);
attrs.namespaces.push_back(cx.publicNamespace());
attrs.namespace_ids.push_back(PROTECTED);
}
else
{
attrs.namespaces.push_back(protected_namespaces.back());
attrs.namespace_ids.push_back(PROTECTED);
}
}
else
{
if( static_protected_namespaces.size() == 0 )
{
cx.error(attrs.pos(), kError_InvalidProtected);
attrs.namespaces.push_back(cx.publicNamespace());
attrs.namespace_ids.push_back(PROTECTED);
}
else
{
attrs.namespaces.push_back(static_protected_namespaces.back());
attrs.namespace_ids.push_back(PROTECTED);
}
}
}
else
if( attrs.hasPublic )
{
if( public_namespaces.size() == 0 )
{
attrs.namespaces.push_back(cx.publicNamespace());
attrs.namespace_ids.push_back(cx.publicNamespace().name);
}
else
{
attrs.namespaces.push_back(public_namespaces.back());
attrs.namespace_ids.push_back(public_namespaces.back().name);
}
}
else
if( attrs.hasInternal )
{
if( public_namespaces.size() == 0 ) // use public namespaces to determine if we are in a valid context for internal
{
attrs.namespaces.push_back(cx.publicNamespace());
attrs.namespace_ids.push_back(cx.publicNamespace().name);
}
else
{
attrs.namespaces.push_back(default_namespaces.back());
attrs.namespace_ids.push_back(default_namespaces.back().name);
}
}
else
if( attrs.namespaces.size() == 0 )
{
attrs.namespaces.push_back(default_namespaces.back());
attrs.namespace_ids.push_back(default_namespaces.back().name);
// We do this here, because ALN is not always evaluated
// in a context where the access control namespaces are
// known. If ALN has not been evaluated, then attrs->namespaces
// will be wrongly empty
}
namespaces.addAll(attrs.namespaces);
namespace_ids.addAll(attrs.namespace_ids);
}
else
{
namespace_ids.push_back(default_namespaces.back().name);
namespaces.push_back(default_namespaces.back());
}
}
public Value evaluate(Context cx, VariableBindingNode node)
{
boolean is_intrinsic = false;
boolean is_const = node.kind == CONST_TOKEN;
Namespaces namespaces = new Namespaces();
ObjectList<String> namespace_ids = new ObjectList<String>();
if( node.attrs != null) // already been evaluated by VariableDefinitionNode
{
if( node.attrs.hasVirtual && node.attrs.hasFinal )
{
cx.error(node.pos(), kError_VarIsFinalAndVirtual);
}
if( node.attrs.hasNative )
{
cx.error(node.pos(), kError_NativeVars);
}
if( node.attrs.hasVirtual )
{
cx.error(node.pos(), kError_VirtualVars);
}
is_intrinsic = node.attrs.hasIntrinsic;
}
if( node.attrs == null && node.variable.type == null )
{
ObjectValue ns = default_namespaces.back();
namespaces.push_back(ns);
namespace_ids.push_back(ns.name);
NodeFactory nf = cx.getNodeFactory();
boolean isPublic = ns == cx.publicNamespace();
AttributeListNode aln = nf.attributeList(nf.identifier(isPublic?PUBLIC:INTERNAL,false,node.variable.pos()),null);
if (isPublic)
{
aln.hasPublic = true;
}
else
{
aln.hasInternal = true;
}
aln.namespaces.push_back(ns);
if( node.variable.identifier instanceof QualifiedIdentifierNode )
{
((QualifiedIdentifierNode)node.variable.identifier).qualifier = aln;
}
else
{
node.variable.identifier = nf.qualifiedIdentifier(aln, node.variable.identifier.name, node.variable.identifier.pos());
}
Value val = node.variable.identifier.evaluate(cx,this);
node.ref = (val instanceof ReferenceValue) ? (ReferenceValue) val : null;
}
else
{
computeNamespaces( cx, node.attrs, namespaces, namespace_ids );
Value val = node.variable.identifier.evaluate(cx,this);
node.ref = (val instanceof ReferenceValue) ? (ReferenceValue) val : null;
if (node.inPackage() == false && cx.getScopes().size() == 1 && node.attrs != null)
{
if( node.attrs.hasAttribute(PUBLIC) )
cx.error(node.attrs.pos(), kError_InvalidPublic);
}
}
Value val = node.variable.identifier.evaluate(cx,this);
node.ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
if (node.initializer != null)
{
if( cx.statics.es4_nullability && cx.scope().builder instanceof InstanceBuilder )
{
// Initializers for instance variables should not have access to this.
cx.scope().setInitOnly(true);
this_contexts.push_back(init_this);
}
node.initializer.evaluate(cx,this);
if( cx.statics.es4_nullability && cx.scope().builder instanceof InstanceBuilder )
{
cx.scope().setInitOnly(false);
this_contexts.pop_back();
}
}
ObjectValue obj = getVariableDefinitionScope(cx);
Builder bui = obj.builder;
int slot_id = -1;
if( bui instanceof InstanceBuilder && node.ref.name.equals(fun_name_stack.back()))
{
cx.error(node.pos(), kError_ConstructorsMustBeInstanceMethods);
}
Namespaces open_definition_namespaces ;
if( node.attrs != null && node.attrs.hasUserNamespace() )
{
open_definition_namespaces = namespaces;
}
else
{
open_definition_namespaces = used_def_namespaces_sets.back();
}
if (node.variable.type != null)
{
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
/* We used to get node.typeref from the result of node.variable.type.eval
* Having changed MemberExprNodes so that they return cx.object().prototype,
* rather than node.ref, this is a workaround to setting the correct typeref: */
val = node.variable.type.evaluate(cx,this);
obj = getVariableDefinitionScope(cx);
if (obj.builder instanceof ActivationBuilder)
{
body_unresolved_sets.last().addAll(unresolved);
}
else
{
ce_unresolved_sets.last().addAll(unresolved);
}
unresolved.clear();
node.typeref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
if( node.typeref != null )
{
node.typeref.setTypeAnnotation(true);
}
else
{
// If the type didn't resolve to a reference value then it's clearly going to be unknown
cx.error(node.variable.type.pos(), kError_UnknownType, "");
}
}
int kind = GET_TOKEN;
Namespaces matchingNamespaces = obj.hasNames(cx,GET_TOKEN,node.ref.name,open_definition_namespaces);
if( matchingNamespaces == null )
{
matchingNamespaces = obj.hasNames(cx, SET_TOKEN, node.ref.name, open_definition_namespaces);
kind = SET_TOKEN;
}
if( matchingNamespaces == null )
{
// Allocate space for the variable and create the property
// slots. A property is represented at compile-time as a
// name and a pair of accessors (getter and setter).
TypeValue type = cx.noType();
// ISSUE: the actual slot type is computed at constanteval time.
// make sure that is never too late
if( bui.is_intrinsic || is_intrinsic )
{
slot_id = bui.ExplicitVar(cx,obj,node.ref.name,namespaces,type,-1);
}
else
{
int var_id;
var_id = bui.Variable(cx,obj);
slot_id = bui.ExplicitVar(cx,obj,node.ref.name,namespaces,type,-1,-1,var_id);
Slot slot = obj.getSlot(cx,slot_id);
slot.setConst(is_const);
slot.setTypeRef(node.typeref);
if( (node.block != null) || // node.block is null for defintions at the top level of the method
(node.initializer == null) )
{
// Need to init the local at the beginning of the method
// so that the types at the backwards branch will match at
// verify time.
slot.setNeedsInit(true);
}
}
}
else
{
String nsstr = "";
for (ObjectValue ns : matchingNamespaces)
{
if (nsstr.length() != 0)
nsstr += " ";
if (ns.name.length() == 0)
{
nsstr += PUBLIC;
}
else
{
switch( ns.getNamespaceKind() )
{
case Context.NS_PRIVATE:
nsstr += PRIVATE;
break;
case Context.NS_INTERNAL:
nsstr += INTERNAL;
break;
case Context.NS_PROTECTED:
nsstr += PROTECTED;
break;
default:
nsstr += ns.name;
break;
}
}
}
int slot_index = obj.getSlotIndex(cx, kind,node.ref.name, matchingNamespaces.back());
Slot orig = obj.getSlot(cx, slot_index);
boolean isGlobalDefinition = bui instanceof GlobalBuilder && !node.inPackage();
boolean isLocalDefinition = bui instanceof ActivationBuilder;
boolean isGlobalOrLocalDefinition = isGlobalDefinition || isLocalDefinition;
if( isGlobalOrLocalDefinition && node.attrs == null && node.variable.type == null )
{
if( orig.getType().getTypeValue() == cx.typeType() || orig.isConst() )
{
// attempting to declare a var with the same name as a class, don't allow that
cx.error(node.variable.identifier.pos(), kError_ConflictingNameInNamespace, node.ref.name, "internal");
}
else
{
// ed.3 decl, so let it go
// need to modify the qualified identifers attribute list, so that the
// qualified identifier node that was auto generated for the init statement will refer to the correct namespace
NodeFactory nf = cx.getNodeFactory();
AttributeListNode aln = nf.attributeList(nf.identifier(matchingNamespaces.back().name,node.variable.pos()),null);
aln.items.clear();
aln.namespaces.addAll(matchingNamespaces);
if( node.variable.identifier instanceof QualifiedIdentifierNode )
{
((QualifiedIdentifierNode)node.variable.identifier).qualifier = aln;
}
else
{
node.variable.identifier = nf.qualifiedIdentifier(aln, node.variable.identifier.name, node.variable.identifier.pos());
}
node.variable.identifier.ref = null; // force this to be regenerated since the namespace has probably changed
Value val2 = node.variable.identifier.evaluate(cx,this);
node.ref = (val instanceof ReferenceValue) ? (ReferenceValue) val2 : null;
}
}
else
if( orig.declaredBy != obj )
{
String fullname = getFullNameForInheritedSlot(cx, orig.declaredBy, node.ref.name);
cx.error(node.variable.identifier.pos(), kError_ConflictingInheritedNameInNamespace, fullname, nsstr);
}
else
{
if( isGlobalOrLocalDefinition && !orig.isConst() && (orig.getTypeRef()==null || node.typeref==null || orig.getTypeRef().name.equals(node.typeref.name)) )
{
// compatible definitions so allow
}
else
{
cx.error(node.variable.identifier.pos(), kError_ConflictingNameInNamespace, node.ref.name, "internal");
}
}
if( (node.block != null) || // node.block is null for defintions at the top level of the method
(node.initializer == null) )
{
// Need to init the local at the beginning of the method
// so that the types at the backwards branch will match at
// verify time.
orig.setNeedsInit(true);
}
}
node.debug_name = cx.debugName(region_name_stack.back(),node.ref.name,namespace_ids,VAR_TOKEN);
return null;
}
/*
Function definitions can occur in the global scope, other function bodies, class definitions,
and package definitions. Inside class definitions they can be one of:
global method on a class object (static)
global method on an instance object (final or ctor method)
local method on an instance object (non-static method)
local getter on an instance object (non-static getter)
local setter on an instnace object (non-static setter)
function closure on the global object (toplevel function)
function closure on a function object (nested function)
A function closure is a method bound to a lexical environement
A method closure is a method bound to a receiver object. One is created when a method is
extracted from its instance.
The FunctionDefinitionNode evaluator is responsible for creating the
appropriate binding and compile-time value. It does this by determining
the correct Builder action to take.
E.g. A static method on a class will dispatch the class object builder
with the sequence: Method, ExplicitMethod.
E.g. A non-static getter will result in the instance builder getting the
commands: Method, ExplicitGet
polymorphic, monomorphic
native, normal
global, local
*/
public Value evaluate(Context unused_cx, FunctionDefinitionNode node)
{
Context cx = node.cx; // switch context to the one used to parse this node, for error reporting
// If this is a toplevel definition (pkgdef!=null), then set up access namespaces
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.push_back(node.pkgdef.publicNamespace);
default_namespaces.push_back(node.pkgdef.internalNamespace);
usednamespaces_sets.push_back(node.pkgdef.used_namespaces);
used_def_namespaces_sets.push_back(node.pkgdef.used_def_namespaces);
importednames_sets.push_back(node.pkgdef.imported_names);
Builder temp_bui = cx.scope().builder;
GlobalBuilder bui = ((temp_bui instanceof GlobalBuilder) ? (GlobalBuilder)temp_bui : null);
if( bui != null )
{
bui.is_in_package = true;
} // otherwise, internal error
}
// Attributes
boolean is_static = false;
boolean is_intrinsic = false;
boolean is_native = false;
boolean is_ctor = false;
boolean is_final = false;
boolean is_override = false;
boolean is_prototype = node.is_prototype;
boolean is_dynamic = false;
Namespaces namespaces = new Namespaces();
ObjectList<String> namespace_ids = new ObjectList<String>(1);
if( node.attrs != null)
{
if( node.attrs.hasVirtual && node.attrs.hasFinal )
{
cx.error(node.attrs.pos(), kError_FuncIsVirtualAndFinal);
}
if( node.attrs.hasStatic && node.attrs.hasVirtual )
{
cx.error(node.attrs.pos(), kError_FuncIsStaticAndVirtual);
}
if( node.attrs.hasStatic && node.attrs.hasOverride )
{
cx.error(node.attrs.pos(), kError_FuncIsStaticAndOverride);
}
if( node.attrs.hasStatic && node.attrs.hasDynamic )
{
cx.error(node.attrs.pos(), kError_InvalidDynamic);
}
is_static = node.attrs.hasStatic;
is_intrinsic = node.attrs.hasIntrinsic;
is_native = node.attrs.hasNative;
is_dynamic = node.attrs.hasDynamic;
if( is_static )
{
is_final = true; // statics are always final
}
else
{
is_final = node.attrs.hasFinal;
}
is_override = node.attrs.hasOverride;
}
computeNamespaces(cx,node.attrs,namespaces,namespace_ids);
if (node.pkgdef == null && cx.getScopes().size() == 1 && node.attrs != null )
{
if( node.attrs.hasAttribute(PUBLIC) )
cx.error(node.attrs.pos(), kError_InvalidPublic);
}
NodeFactory nodeFactory = cx.getNodeFactory();
QualifiedIdentifierNode qualifiedIdentifier = nodeFactory.qualifiedIdentifier(node.attrs, node.name.identifier.name, node.name.identifier.pos());
node.init = nodeFactory.expressionStatement(nodeFactory.assignmentExpression(qualifiedIdentifier, CONST_TOKEN, node.fexpr));
node.init.isVarStatement(true); // var statements always have a empty result
// Compute reference
boolean is_first_time = node.ref == null;
Value val = node.name.identifier.evaluate(cx,this);
node.ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
node.fexpr.namespace_ids = namespace_ids;
// Get the current object and its builder
ObjectValue obj = getVariableDefinitionScope(cx);
Builder bui = obj.builder;
region_name_stack.push_back(cx.debugName(region_name_stack.back(),node.ref.name,namespace_ids,node.name.kind));
// Constructor? Tweak the name
String cname = fun_name_stack.back();
if( cname.equals(node.ref.name))
{
if( bui instanceof InstanceBuilder)
{
is_ctor = true;
node.ref.name = "$construct";
is_final = true; // not strictly speaking, but can't be hidden, can't be overriden
namespaces.push_back(cx.publicNamespace());
}
else
{
cx.error(node.pos(), kError_ConstructorsMustBeInstanceMethods);
}
if( node.name.kind == SET_TOKEN || node.name.kind == GET_TOKEN)
{
cx.error(node.pos(), kError_ConstructorCannnotBeGetterSetter);
}
if( node.attrs != null )
{
for ( ObjectValue ns : node.attrs.namespaces )
{
if( ns != cx.publicNamespace() )
{
cx.error(node.pos(), kError_ConstructorMustBePublic);
break;
}
}
}
}
if (bui instanceof ActivationBuilder)
{
if (node.name.kind == SET_TOKEN || node.name.kind == GET_TOKEN)
{
cx.error(node.pos(), kError_InvalidNestedAccessor);
}
}
boolean is_interface_method = false;
boolean is_instance_method = false;
if( bui instanceof InstanceBuilder )
{
is_instance_method = true;
is_interface_method = (obj.type != null && obj.type.isInterface());
}
else if( bui instanceof ClassBuilder)
{
if( ((ClassBuilder)bui).is_interface )
{
is_interface_method = true;
}
}
if( is_interface_method || is_native )
{
if (is_interface_method && is_native)
{
cx.error(node.pos(), kError_InvalidInterfaceNative);
}
if( node.fexpr.isUserDefinedBody() )
{
cx.error(node.pos(), is_interface_method ? kError_InterfaceMethodWithBody : kError_NativeMethodWithBody);
}
}
else
{
if( !node.fexpr.isUserDefinedBody() && !is_ctor && !is_native && !is_dynamic ) //ctors and native and dynamic methods don't need bodies
{
cx.error(node.pos(), kError_FunctionWithoutBody);
}
}
if( node.attrs != null )
{
if( node.attrs.hasFinal && (!is_instance_method || is_interface_method) )
{
cx.error(node.pos(), kError_InvalidFinalUsage);
}
if( is_interface_method && (node.attrs.hasPrivate || node.attrs.hasProtected || node.attrs.hasInternal || node.attrs.hasPublic) && (!node.ref.name.equals("$construct") ))
{
// todo fix error msg
cx.error(node.pos(), kError_BadAccessInterfaceMember);
}
}
if( is_static )
{
if (is_interface_method)
{
cx.error(node.pos(), kError_InvalidStatic);
}
}
if (is_interface_method)
{
// Namespace attributes are not allowed on interface methods
if (node.attrs != null && node.attrs.getUserNamespace() != null)
{
cx.error(node.pos(), kError_InterfaceNamespaceAttribute);
}
}
// If the method is public and matches an interface method name, add
// the interface namespaces.
else if (interfaceMethods != null &&
interfaceMethods.size() > 0 &&
namespaces.contains(cx.publicNamespace()))
{
Qualifiers q;
if(Builder.removeBuilderNames) // TODO: {pmd} both ways on this if look very similar, review this
{
q = interfaceMethods.get(node.ref.name, node.name.kind == SET_TOKEN ? Names.SET_NAMES : Names.GET_NAMES );
}
else
{
q = interfaceMethods.get(node.ref.name, Names.getTypeFromKind(node.name.kind));
}
if(q != null)
{
for (ObjectValue ns : q.keySet())
{
namespaces.push_back(ns);
namespace_ids.push_back(ns.name);
}
}
}
/*
Define a Call slot on the current frame. There are three possible
interpretations for this node:
1/ it overrides an inherited method - reuse the inherited explicit binding
give it a new implicit binding
2/ it overrides an non-inherited method - if it is in a strict context, then
report an error, otherwise give it a new implicit binding
3/ it introduces a new method - give it a new explicit binding
*/
int slot_id = -1;
int kind = node.name.kind == SET_TOKEN?SET_TOKEN:GET_TOKEN;
Namespaces open_definition_namespaces ;
if( node.attrs != null && node.attrs.hasUserNamespace())
{
open_definition_namespaces = namespaces;
}
else
{
open_definition_namespaces = used_def_namespaces_sets.back();
}
Namespaces hasNamespaces = obj.hasNames(cx,kind ,node.ref.name,open_definition_namespaces);
if( hasNamespaces != null )
{
// Can only override instance methods
if( bui instanceof InstanceBuilder )
{
slot_id = obj.getSlotIndex(cx,kind,node.ref.name,hasNamespaces.back());
// ISSUE: need to check that all names are to the same slot
// Get the implicit method slot to get the default method_id
int implied_id = obj.getImplicitIndex(cx,slot_id,EMPTY_TOKEN);
// If slot id is less than zero, then this is a getter or setter. Getter
// and setter method ids are encoded in their explicit slot, so nothing
// do do here
if( implied_id >= 0 ) // else, do nothing
{
// check that the override is at least as accessible as the overriden
if (!is_ctor && !namespacesContains(cx, namespaces, hasNamespaces))
{
if( !( namespaces.size() == 1 && hasNamespaces.size() == 1 && namespaces.at(0).isProtected() && hasNamespaces.at(0).isProtected() ) )
{
cx.error(node.pos(), kError_IncompatibleOverride);
}
}
int overridden_kind = (slot_id == implied_id) ? kind : EMPTY_TOKEN;
if (overridden_kind != node.name.kind && !is_ctor)
{
cx.error(node.pos(), kError_IncompatibleOverride);
}
if( true /* check signature and final */ )
{
Slot slot = obj.getSlot(cx,implied_id);
is_dynamic = slot.isIntrinsic();
int method_id = slot.getMethodID();
if( slot.isFinal() && !is_ctor )
{
if( node.name.kind == SET_TOKEN || node.name.kind == GET_TOKEN)
cx.error(node.pos(), kError_OverrideFinalAccessor);
else
cx.error(node.pos(), kError_FinalMethodRedefinition);
}
if( slot.declaredBy == obj )
{
// This was already defined at this level, it was not inherited from a base class
cx.error(node.pos(), kError_DuplicateFunction);
}
else if( is_prototype || is_dynamic )
{
is_override = true;
}
else if( !is_override && !is_ctor )
{
cx.error(node.pos(), kError_OverrideOfFuncNotMarkedForOverride);
}
if( node.name.kind == GET_TOKEN )
{
slot_id = bui.ExplicitGet(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
else
if( node.name.kind == SET_TOKEN )
{
slot_id = bui.ExplicitSet(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
else
{
slot_id = bui.ExplicitCall(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
if( !is_ctor )
{
// Constructors don't actually override the base class constructor
// Can have different signatures, so don't mark it as having an overriden slot
// so we won't do signature matching later.
Slot overriddenSlot = slot;
Slot overrideSlot = obj.getSlot(cx,obj.getImplicitIndex(cx,slot_id,EMPTY_TOKEN));
overrideSlot.setOverriddenSlot(overriddenSlot);
}
}
else
{
cx.error(node.pos(), kError_IncompatibleOverride);
}
}
else // cn: I think accessors now always end up in the block above, making this else block obsolete.
{
Slot slot = obj.getSlot(cx,slot_id);
if( slot.getMethodID() <= 0 )
{
cx.error(node.pos(), kError_OverrideFinalAccessor);
}
// ISSUE: implement accessor overriding
}
}
else
if( bui instanceof ClassBuilder )
{
cx.error(node.pos(), kError_DuplicateFunction);
}
else
if (bui instanceof GlobalBuilder && (node.pkgdef != null || namespaces.at(0) != hasNamespaces.at(0)) && is_first_time )
{
cx.error(node.pos(), kError_DuplicateFunction);
}
else
if( cx.useStaticSemantics() && is_first_time ) // ISSUE: remove use of this flag by not evaluating this code twice
{
cx.error(node.pos(), kError_DuplicateFunction);
}
else
{
slot_id = obj.getSlotIndex(cx,GET_TOKEN,node.ref.name,hasNamespaces.at(0));
}
}
else
{
if( is_override && !is_ctor)
{
if (is_interface_method)
{
cx.error(node.pos(), kError_InvalidOverrideUsage);
}
else
{
ObjectValue n = namespaces.at(0);
UnresolvedNamespace un = n instanceof UnresolvedNamespace ? (UnresolvedNamespace) n : null;
if( un == null || un.resolved )
{
cx.error(node.pos(), kError_OverrideNotFound);
}
else
{
cx.error(un.node.pos(), kError_Unknown_Namespace);
}
}
}
if( node.name.kind == GET_TOKEN )
{
int method_id = bui.Method(cx,obj,(node.ref.name+"$get").intern(),namespaces,is_intrinsic); // Add getter to local dispatch table
slot_id = bui.ExplicitGet(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
else
if( node.name.kind == SET_TOKEN )
{
int method_id = bui.Method(cx,obj,(node.ref.name+"$set").intern(),namespaces,is_intrinsic);
slot_id = bui.ExplicitSet(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
else
{
int method_id = bui.Method(cx,obj,node.ref.name,namespaces,is_intrinsic);
slot_id = bui.ExplicitCall(cx,obj,node.ref.name,namespaces,cx.noType(),is_final,is_override,-1,method_id,-1);
}
}
/*
At this point we have either reported a redefinition error, or have
the explicit slot for the function being defined, and an implicit
slot for the implementation (i.e. method name, call seq)
Now we specify the implementation
*/
if( is_intrinsic )
{
}
else
if( node.fexpr != null && slot_id >= 0 )
{
node.fexpr.setNative(is_native);
node.fexpr.kind = node.name.kind; // inherited attribute
Slot slot = obj.getSlot(cx,slot_id);
// FunctionCommonNode gets evaluated twice. The first time is for initializing
// the function object and adding it to the list of nodes to be evaluated later.
// The second time (this time) is for evaluating the function body.
val = node.fexpr.evaluate(cx,this);
val = val != null ? val.getValue(cx) : null;
slot.setObjectValue((val instanceof ObjectValue ? (ObjectValue)val : null));
// slot.objValue = dynamic_cast<ObjectValue>(val!=null?val.getValue(cx) : null);
if( slot.getObjectValue() != null)
{
// Resolve this function to its local dispatch id, if it is virtual, or a global
// method id (method info) if it is not. Local method ids share the same local
// name (the name of the original method). If B overrides m in A, then the local
// method id for B.m will be something like A$m. If B.n is new, then its local
// name will be something like B$n. A name consists of the classname,simplename,and
// namespace.
// B inherits the names of A. B declares an override of A.m. The new method slot for
// m in B has the internal name that is the same as m in A, and therefore the same
// dispatch id ( = local method name id).
// The key is comparing names so that the rules for overriding are correctly implemented.
if( is_ctor )
{
InstanceBuilder ib = ((bui instanceof InstanceBuilder) ? (InstanceBuilder)bui : null);
if( ib != null)
{
ib.has_ctor = true;
ib.ctor_name = node.fexpr.internal_name;
}
else
{
cx.error(node.pos(), kError_ConstructorsMustBeInstanceMethods);
}
}
else
if( bui instanceof ClassBuilder || bui instanceof InstanceBuilder || bui instanceof PackageBuilder )
{
}
else
{
}
/*
Copy the implementation details into the implicit call slot
*/
slot_id = obj.getImplicitIndex(cx,slot_id,EMPTY_TOKEN);
obj.getSlot(cx,slot_id).setMethodName(node.fexpr.internal_name);
obj.getSlot(cx,slot_id).setIntrinsic(is_dynamic);
slot.getObjectValue().name = node.fexpr.internal_name;
}
node.fexpr.debug_name = region_name_stack.back();
}
region_name_stack.pop_back();
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
usednamespaces_sets.pop_back();
used_def_namespaces_sets.pop_back();
importednames_sets.pop_back();
public_namespaces.pop_back();
default_namespaces.pop_back();
Builder temp_bui = cx.scope().builder;
GlobalBuilder gbui = ((temp_bui instanceof GlobalBuilder) ? (GlobalBuilder)temp_bui : null);
if( gbui != null )
{
gbui.is_in_package = false;
} // otherwise, internal error
}
if( node.needs_init )
{
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
usednamespaces_sets.push_back(node.pkgdef.used_namespaces);
used_def_namespaces_sets.push_back(node.pkgdef.used_def_namespaces);
importednames_sets.push_back(node.pkgdef.imported_names);
public_namespaces.push_back(node.pkgdef.publicNamespace);
default_namespaces.push_back(node.pkgdef.internalNamespace);
Builder temp_bui = cx.scope().builder;
GlobalBuilder gbui = ((temp_bui instanceof GlobalBuilder) ? (GlobalBuilder)temp_bui : null);
if( gbui != null )
{
gbui.is_in_package = true;
} // otherwise, internal error
}
node.init.evaluate(cx,this);
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.pop_back();
default_namespaces.pop_back();
usednamespaces_sets.pop_back();
used_def_namespaces_sets.pop_back();
importednames_sets.pop_back();
Builder temp_bui = cx.scope().builder;
GlobalBuilder gbui = ((temp_bui instanceof GlobalBuilder) ? (GlobalBuilder)temp_bui : null);
if( gbui != null )
{
gbui.is_in_package = false;
} // otherwise, internal error
}
return null;
}
return null;
}
public Value evaluate(Context unused_cx, BinaryFunctionDefinitionNode node)
{
return null;
}
public Value evaluate(Context cx, FunctionNameNode node)
{
Value val = node.identifier.evaluate(cx, this);
ReferenceValue ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
return ref;
}
public Value evaluate(Context cx, FunctionSignatureNode node)
{
if (node.parameter != null)
{
node.parameter.evaluate(cx, this);
}
if (node.result != null)
{
Value val = node.result.evaluate(cx,this);
node.typeref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
if( node.typeref == null )
{
cx.error(node.result.pos(), kError_UnknownType);
}
else
{
node.typeref.setTypeAnnotation(true);
}
}
return null;
}
public Value evaluate( Context cx, RestParameterNode node )
{
ObjectValue obj = cx.scope();
Builder bui = obj.builder;
Value v = node.identifier.evaluate(cx,this);
node.ref = ((v instanceof ReferenceValue) ? (ReferenceValue)v : null);
if( node.type != null)
{
v = node.type.evaluate(cx,this);
node.typeref = ((v instanceof ReferenceValue) ? (ReferenceValue)v : null);
if( node.typeref != null )
{
node.typeref.setTypeAnnotation(true);
}
}
Namespaces namespaces = new Namespaces();
namespaces.push_back(cx.publicNamespace());
{
// Allocate space for the variable and create the property
// slots. A property is represented at compile-time as a
// name and a pair of accessors (getter and setter).
int var_id;
var_id = bui.Variable(cx,obj);
bui.ExplicitVar(cx,obj,node.ref.name,namespaces,cx.arrayType(),-1,-1,var_id);
}
return node.ref;
}
public Value evaluate( Context cx, ParameterNode node )
{
ObjectValue obj = cx.scope();
Builder bui = obj.builder;
Value v = node.identifier.evaluate(cx,this);
node.ref = ((v instanceof ReferenceValue) ? (ReferenceValue)v : null);
if (node.init != null)
{
node.init.evaluate(cx, this);
}
if( node.type != null)
{
v = node.type.evaluate(cx,this);
node.typeref = ((v instanceof ReferenceValue) ? (ReferenceValue)v : null);
if( node.typeref != null )
{
node.typeref.setTypeAnnotation(true);
}
else
{
cx.error(node.type.pos(), kError_UnknownType);
}
}
Namespaces namespaces = new Namespaces();
ObjectValue default_ns = default_namespaces.last();
if (default_ns.isInterface())
{
// parameters of interface methods should be public
default_ns = cx.publicNamespace();
}
namespaces.push_back(default_ns);
{
// Allocate space for the variable and create the property
// slots. A property is represented at compile-time as a
// name and a pair of accessors (getter and setter).
int var_id;
var_id = bui.Variable(cx,obj);
int slot_id = bui.ExplicitVar(cx,obj,node.ref.name,namespaces,cx.noType(),-1,-1,var_id);
Slot slot = obj.getSlot(cx,slot_id);
slot.setTypeRef(node.typeref);
}
return node.ref;
}
public Value evaluate( Context cx, ParameterListNode node )
{
if( debug )
{
System.out.println("\n// +ParameterList");
}
for (ParameterNode it : node.items)
{
it.evaluate(cx,this);
}
if( debug )
{
System.out.println("\n// -ParameterList");
}
return null;
}
public Value evaluate(Context cx, ToObjectNode node)
{
node.expr.evaluate(cx, this);
return null;
}
public Value evaluate(Context cx, LoadRegisterNode node)
{
if( node.reg != null )
{
node.reg.evaluate(cx,this);
}
return null;
}
public Value evaluate(Context cx, StoreRegisterNode node)
{
if( node.reg != null )
{
node.reg.evaluate(cx,this);
}
node.expr.evaluate(cx, this);
return null;
}
public Value evaluate(Context cx, RegisterNode node)
{
return null;
}
public Value evaluate(Context cx, HasNextNode node)
{
return null;
}
public Value evaluate(Context cx, BoxNode node)
{
return null;
}
public Value evaluate(Context cx, CoerceNode node)
{
if (node.expr != null)
{
node.expr.evaluate(cx, this);
}
return null;
}
/*
* Processing class definitions involves separating the static definitions
* from the non-statics. The statics go into an outer function, and the
* non-statics go into an inner function.
*
* The trick is to complete the static definitions before the non-statics.
* This is neccessary because the statics are in scope for the non-statics.
*/
public Value evaluate(Context unused_cx, ClassDefinitionNode node)
{
// If we are doing a class, then defer this class definition until we
// are done. Put it in the current set of the clsdefs_sets for now.
Context cx = node.cx; // switch contexts so that the original one is used
/* #if 0 // debugging
switch( node.state )
{
case node.INIT:
printf("\ndoing init");
break;
case node.INHERIT:
printf("\ndoing inheritance");
break;
case node.MAIN:
printf("\ndoing body");
break;
default:
cx.internalError("invalid CDN state");
break;
}
#endif */
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.push_back(node.pkgdef.publicNamespace);
default_namespaces.push_back(node.pkgdef.internalNamespace);
usednamespaces_sets.push_back(node.pkgdef.used_namespaces);
used_def_namespaces_sets.push_back(node.pkgdef.used_def_namespaces);
importednames_sets.push_back(node.pkgdef.imported_names);
}
// First, initialize the node
if( node.cframe == null /*doingClass()*/ )
{
// If this is a toplevel definition (pkgdef!=null), then set up access namespaces
node.used_namespaces.addAll(usednamespaces_sets.back()); // makes a copy
node.imported_names.putAll(importednames_sets.back()); // makes a copy
node.public_namespace = cx.publicNamespace(); // public_namespaces.back();
node.default_namespace = default_namespaces.back();
// clsdefs_sets is a stack of sets of class definitions.
// Each set contains the classes at a particular scope level.
if( clsdefs_sets.size() == 1 ) // otherwise, we've already captured the clsdefs for nested classes
{
int size = clsdefs_sets.last().size();
int i;
// Look for the current node in the set for the current scope.
for (i = 0; i < size && clsdefs_sets.last().get(i) != node; ++i);
// If it is not in the set, then add it.
if (i >= size)
{
if (package_name.length() != 0)
{
node.package_name = package_name;
}
clsdefs_sets.last().add(node);
}
else
{
//cx.internalError("Internal error: the same class definition should never get processed twice.");
// This actually does happen, but the code from this point on is only executed once.
}
}
// boolean is_static = false;
boolean is_intrinsic = false;
if (node.attrs != null)
{
// is_static = node.attrs.hasStatic;
is_intrinsic = node.attrs.hasIntrinsic;
if( node.attrs.hasNative )
{
cx.error(node.pos(), kError_InvalidNative);
}
// Note: node.attrs.hasOverride will have already been checked
// by the hoisted_defs test in StatementListNode
/*if( node.attrs.hasOverride )
{
cx.error(node.pos(), kError_InvalidOverride);
}*/
}
if( cx.getScopes().size() > 1 )
{
if (node.isInterface())
cx.error(node.pos(), kError_InvalidInterfaceNesting);
else
cx.error(node.pos(), kError_InvalidClassNesting);
}
// Only do the following once
if (node.ref == null)
{
ObjectValue ownerobj = cx.scope();
Builder ownerbui = ownerobj.builder;
String region_name = region_name_stack.back();
region_name += region_name.length() > 0 ? "/" : "";
ObjectList<String> namespace_ids = new ObjectList<String>();
computeNamespaces(cx,node.attrs,node.namespaces,namespace_ids);
if (node.pkgdef == null && cx.getScopes().size() == 1 && node.attrs != null)
{
if( node.attrs.hasAttribute(PUBLIC) )
cx.error(node.attrs.pos(), kError_InvalidPublic);
}
Value val = node.name.evaluate(cx,this);
node.ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
//String fullname = cx.debugName(region_name,node.ref.name,namespace_ids,EMPTY_TOKEN);
QName fullname = cx.computeQualifiedName(region_name, node.ref.name, node.ref.getImmutableNamespaces().back(), EMPTY_TOKEN);
node.private_namespace = cx.getNamespace(fullname.toString(), Context.NS_PRIVATE);
node.protected_namespace = cx.getNamespace(fullname.toString(), Context.NS_PROTECTED);
node.static_protected_namespace = cx.getNamespace(fullname.toString(), Context.NS_STATIC_PROTECTED);
if( cx.isBuiltin(fullname.toString()) )
{
node.cframe = cx.builtin(fullname.toString());
node.iframe = node.cframe.prototype;
}
else
{
node.cframe = TypeValue.defineTypeValue(cx,new ClassBuilder(fullname,node.protected_namespace,node.static_protected_namespace),fullname,TYPE_object); // ISSUE: what should the type tag be?
if( !node.is_default_nullable )
{
node.cframe.is_nullable = false;
}
node.cframe.type = cx.typeType().getDefaultTypeInfo();
if( node.cframe.prototype != null )
{
node.cframe.prototype.clearInstance(cx,new InstanceBuilder(fullname),node.cframe, ObjectValue.EMPTY_STRING, true);
node.iframe = node.cframe.prototype;
}
else
{
node.iframe = new ObjectValue(cx,new InstanceBuilder(fullname),node.cframe);
node.cframe.prototype = node.iframe; // ISSUE: this is not really the prototype, but works for now
node.owns_cframe = true; // so it deletes it
}
}
if (node instanceof InterfaceDefinitionNode)
{
node.default_namespace = node.cframe; // class object and namespace all in one
}
if( node.attrs != null)
{
node.cframe.builder.is_dynamic = node.iframe.builder.is_dynamic = node.attrs.hasDynamic;
node.cframe.builder.is_final = node.iframe.builder.is_final = node.attrs.hasFinal;
node.cframe.builder.is_intrinsic = node.iframe.builder.is_intrinsic = is_intrinsic;
}
Namespaces open_definition_namespaces ;
if( node.attrs != null && node.attrs.hasUserNamespace() )
{
open_definition_namespaces = node.namespaces;
}
else
{
open_definition_namespaces = used_def_namespaces_sets.back();
}
Namespaces hasNamespaces = ownerobj.hasNames(cx,GET_TOKEN,node.ref.name,open_definition_namespaces);
if( hasNamespaces == null )
{
// If this class is intrinsic, then don't implement it
int var_id = -1;
if( node.attrs==null || !node.attrs.hasIntrinsic )
{
var_id = ownerbui.Variable(cx,ownerobj);
}
int slot_id = ownerbui.ExplicitVar(cx,ownerobj,node.ref.name,node.namespaces,cx.typeType(),-1,-1,var_id);
ownerobj.getSlot(cx,slot_id).setValue(node.cframe);
ownerobj.getSlot(cx,slot_id).setConst(true); // class defs are const.
// Implicit method to represent call & construct
ownerbui.ImplicitCall(cx,ownerobj,slot_id,node.cframe,CALL_Method,-1,-1);
ownerbui.ImplicitConstruct(cx,ownerobj,slot_id,node.cframe,CALL_Method,-1,-1);
}
else
{
if( node.isInterface() )
{
cx.error( node.name.pos(), kError_DuplicateInterfaceDefinition, node.ref.name);
}
else
{
cx.error( node.name.pos(), kError_DuplicateClassDefinition, node.ref.name);
found_circular_or_duplicate_class_definition = true;
}
}
// delete hasNamespaces;
}
node.used_namespaces.push_back(node.private_namespace);
node.used_namespaces.push_back(node.protected_namespace);
node.used_namespaces.push_back(node.static_protected_namespace);
node.used_def_namespaces.push_back(node.private_namespace);
node.used_def_namespaces.push_back(node.public_namespace);
node.used_def_namespaces.push_back(node.default_namespace);
node.used_def_namespaces.push_back(node.protected_namespace);
node.used_def_namespaces.push_back(node.static_protected_namespace);
node.state = ClassDefinitionNode.INHERIT;
NodeFactory nodeFactory = cx.getNodeFactory();
QualifiedIdentifierNode qualifiedIdentifier = nodeFactory.qualifiedIdentifier(node.attrs, node.name.name, node.name.pos());
node.init = nodeFactory.expressionStatement(nodeFactory.assignmentExpression(qualifiedIdentifier, CONST_TOKEN, node));
node.init.isVarStatement(true);
clsdefs_sets.push_back(new ObjectList<ClassDefinitionNode>()); // make dummy
cx.pushStaticClassScopes(node);
ObjectList<String> namespace_ids = new ObjectList<String>();
if( node.namespaces.size() != 0 )
{
namespace_ids.push_back(node.namespaces.back().name);
}
else
{
namespace_ids.push_back("error");
}
region_name_stack.push_back(cx.debugName(region_name_stack.back(),node.ref.name,namespace_ids,EMPTY_TOKEN));
usednamespaces_sets.push_back(node.used_namespaces);
used_def_namespaces_sets.push_back(node.used_def_namespaces);
for (ClassDefinitionNode n : node.clsdefs)
{
// Haven't done the outer class' statement list yet, so the attrs
// haven't been done yet
if( n.attrs != null )
{
n.attrs.evaluate(cx,this);
}
n.evaluate(cx,this);
}
cx.popStaticClassScopes(node);
region_name_stack.removeLast();
usednamespaces_sets.removeLast();
used_def_namespaces_sets.removeLast();
clsdefs_sets.removeLast();
}
else if( doingClass() || doingMethod() )
{
// Wait
}
else if (resolveInheritance)
{
if (node.baseclass == null && node.cframe != cx.objectType())
{
NodeFactory nf = cx.getNodeFactory();
node.baseclass = nf.memberExpression(null, nf.getExpression(nf.identifier("Object")));
}
if (node.baseref == null)
{
if (node.baseclass != null)
{
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
Value val2 = node.baseclass.evaluate(cx,this);
fa_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
node.baseref = ((val2 instanceof ReferenceValue) ? (ReferenceValue)val2 : null);
if( node.baseref == null )
{
// uh oh, didn't resolve to anything, but we have a baseclass expression
cx.error(node.baseclass.pos(), kError_InvalidBaseTypeExpression);
}
}
}
if (node.baseref != null)
{
Value val = node.baseref.getValue(cx);
TypeValue type = ((val instanceof TypeValue) ? (TypeValue)val : null);
if (type == null)
{
// stay silent. we'll report this in the else part...
// cx.error(node.baseclass.pos(), kError_UnknownBaseClass);
}
else
if( type.builder.is_final )
{
// stay silent. we'll report this in the else part...
// cx.error(node.baseclass.pos(), kError_BaseClassIsFinal);
}
else
if ( type.builder instanceof ClassBuilder && ((ClassBuilder)type.builder).is_interface )
{
// stay silent. we'll report this in the else part...
// cx.error(node.baseclass.pos(), node.isInterface() ? kError_CannotExtendClass : kError_CannotExtendInterface);
}
else
{
inheritClassSlots(node.cframe, node.iframe, type, cx);
}
}
if( node.interfaces != null )
{
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
node.interfaces.evaluate(cx,this);
fa_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
}
/*
if (node.interfaces != null && node.interfaces.values != null)
{
for (Value v : node.interfaces.values)
{
if (v instanceof ReferenceValue)
{
ReferenceValue ref = (ReferenceValue) v;
Value v2 = v.getValue(cx);
TypeValue t = dynamic_cast(TypeValue.class, v2);
if (t == null)
{
// stay silent. we'll report this in the else part...
// cx.error(node.baseclass.pos(), kError_UnknownBaseClass);
}
else
{
//inheritClassSlots(node.cframe, node.iframe, t, cx);
}
}
}
}
*/
node.state = ClassDefinitionNode.MAIN;
for (ClassDefinitionNode n : node.clsdefs)
{
n.evaluate(cx,this);
}
}
else
if( node.needs_init )
{
node.needs_init = false;
node.init.evaluate(cx,this);
node.needs_init = true;
}
else
{
// Start compiling the class. Statics get put in cframe, everything else
// gets put in iframe.
this_contexts.add(error_this);
strict_context.push_back(true);
usednamespaces_sets.push_back(node.used_namespaces);
used_def_namespaces_sets.push_back(node.used_def_namespaces);
importednames_sets.push_back(node.imported_names);
// Put super instance properties in the instance prototype before compiling
// the current class body.
if (node.baseref != null)
{
Value val = node.baseref.getValue(cx);
TypeValue type = ((val instanceof TypeValue) ? (TypeValue)val : null);
if (type == null)
{
cx.error(node.baseclass.pos(), kError_UnknownBaseClass, node.baseref.name);
}
else
if( type.builder.is_final )
{
cx.error(node.baseclass.pos(), kError_BaseClassIsFinal);
}
else
if ( type.builder instanceof ClassBuilder && ((ClassBuilder)type.builder).is_interface )
{
cx.error(node.baseclass.pos(), kError_CannotExtendInterface);
}
else
{
inheritClassSlots(node.cframe, node.iframe, type, cx);
// No matter what, if the base slot was from an import, we can't early bind.
Slot base_slot = node.baseref.getSlot(node.cx, node.baseref.getKind());
if( base_slot.isImported() && type != cx.noType() ) //Ok if it's object, doesn't have any methods...
{
((InstanceBuilder)node.iframe.builder).canEarlyBind = false;
}
// inherit protected namespaces
ClassBuilder classBuilder;
while( type != null && type != node.cframe && type.resolved)
{
classBuilder = (ClassBuilder) type.builder;
if( classBuilder.static_protected_namespace != null )
{
node.used_namespaces.push_back(classBuilder.static_protected_namespace);
node.used_def_namespaces.push_back(classBuilder.static_protected_namespace);
}
type = type.baseclass;
}
}
}
if (node.interfaces != null && node.interfaces.values != null)
{
ObjectList<ReferenceValue> interface_refs = ((InstanceBuilder)node.iframe.builder).interface_refs;
HashSet<TypeValue> seen_interfs = new HashSet<TypeValue>();
for (int i = 0; i < node.interfaces.values.size(); ++i )
{
Value v = node.interfaces.values.get(i);
if (v instanceof ReferenceValue)
{
ReferenceValue ref = (ReferenceValue) v;
Value v2 = v.getValue(cx);
TypeValue t = ((v2 instanceof TypeValue) ? (TypeValue)v2 : null);
if (t == null )
{
cx.error(node.interfaces.items.get(i).pos(), kError_UnknownInterface, ref.name);
}
else
{
if (t.builder instanceof ClassBuilder)
{
if (!(((ClassBuilder)t.builder).is_interface))
{
cx.error(node.interfaces.items.get(i).pos(), kError_CannotExtendClass, ref.name);
}
else
{
if( seen_interfs.contains(t) )
{
cx.error(node.interfaces.items.get(i).pos(), kError_DuplicateImplements, node.ref.name, ref.name);
}
else
{
seen_interfs.add(t);
}
interface_refs.push_back(ref);
if (node instanceof InterfaceDefinitionNode)
{
// If this is an interface, inherit the super-interface slots.
inheritClassSlots(node.cframe, node.iframe, t, cx);
}
}
}
else
{
cx.error(node.interfaces.items.get(i).pos(), kError_UnknownInterface, ref.name);
}
}
}
else
{
// uh oh, didn't resolve to anything, but we have a baseclass expression
cx.error(node.interfaces.items.get(i).pos(), kError_InvalidInterfaceTypeExpression);
}
}
}
Names lastInterfaceMethods = interfaceMethods;
interfaceMethods = null;
scanInterfaceMethods(cx, node);
processInterfacePublicMethods(cx, node.iframe);
StartClass(node.ref.name);
ObjectList<String> namespace_ids = new ObjectList<String>();
if( node.namespaces.size() != 0 )
{
namespace_ids.push_back(node.namespaces.back().name);
}
else
{
namespace_ids.push_back("error");
}
region_name_stack.push_back(cx.debugName(region_name_stack.back(),node.ref.name,namespace_ids,EMPTY_TOKEN));
/*
node->used_namespaces = *used_namespaces_sets.back() // save alias of outer namespaces
...
node->used_namespaces.push_back(node->private_namespace); // add implicitly used namespaces
...
usednamespaces_sets.back(&node->used_namespaces) // add current namespaces to nss sets
...
used_namespaces get deleted
*/
private_namespaces.push_back(node.private_namespace);
default_namespaces.push_back(node.default_namespace);
public_namespaces.push_back(node.public_namespace);
protected_namespaces.push_back(node.protected_namespace);
static_protected_namespaces.push_back(node.static_protected_namespace);
cx.pushStaticClassScopes(node);
this_contexts.removeLast();
this_contexts.add(cinit_this);
// Function expressions that occur in the current block will be
// compiled as though they had occured at the end of the block.
// The variable that references them is initialized at the beginning
// of the block.
fexprs_sets.add(new ObjectList<FunctionCommonNode>());
staticfexprs_sets.add(new ObjectList<FunctionCommonNode>());
instanceinits_sets.add(new ObjectList<Node>());
// Copy the set of nested functions into the node for use
// by later phases.
node.fexprs = fexprs_sets.last();
node.instanceinits = instanceinits_sets.last(); // Holds the static initializers for this class
node.staticfexprs = staticfexprs_sets.last(); // Holds the static initializers for this class
fun_name_stack.add(node.ref.name); // During flow analysis we use the class name
max_params_stack.add(0);
max_locals_stack.add(node.var_count);
max_temps_stack.add(node.temp_count);
StartMethod(fun_name_stack.last(), max_params_stack.last(), max_locals_stack.last());
if (node.statements != null)
{
// Evaluate the statements. When we are done, the static names
// are in the class object builder. The static initializers are
// in the inner staticdefs_sets sets. The instance names are in
// the instance object builder, and the instance initializers
// are in the
node.statements.evaluate(cx, this);
node.temp_count = getTempCount();
node.var_count = node.cframe.var_count;
}
else
{
StartMethod(fun_name_stack.last(), max_params_stack.last(), max_locals_stack.last());
}
node.temp_count = getTempCount(); // Remember the temp count
// Return(TYPE_none);
Return(TYPE_void);
FinishMethod(cx, fun_name_stack.back(), null,null,null,0, cx.getScopes().size(), "",false,false, null);
cx.pushScope(node.iframe);
this_contexts.removeLast();
this_contexts.add(instance_this);
// Evaluate the instance initializers
// (This must be done before we add the default
// constructor if needed, because this is where
// has_ctor gets set)
{
for (Node n : node.instanceinits)
{
if( cx.statics.es4_nullability && !n.isDefinition())
node.iframe.setInitOnly(true);
n.evaluate(cx, this);
if( cx.statics.es4_nullability && !n.isDefinition())
node.iframe.setInitOnly(false);
}
}
ObjectValue obj = node.iframe;
InstanceBuilder bui = ((obj.builder instanceof InstanceBuilder) ? (InstanceBuilder)obj.builder : null);
if( !bui.is_intrinsic && !bui.has_ctor )
{
NodeFactory nf = cx.getNodeFactory();
FunctionNameNode fname = nf.functionName(EMPTY_TOKEN, nf.identifier(node.ref.name,0));
nf.has_rest = false;
nf.has_arguments = false;
FunctionCommonNode fexpr = nf.functionCommon(cx, fname.identifier, nf.functionSignature(null, null, 0), null, 0);
AttributeListNode attrs = nf.attributeList(nf.identifier(PUBLIC,false,0),null);
attrs.evaluate(cx,this);
FunctionDefinitionNode fdef = nf.functionDefinition(cx, attrs, fname, fexpr);
fdef.pkgdef = node.pkgdef;
fdef.evaluate(cx,this);
Node init = fdef.initializerStatement(cx);
init.evaluate(cx,this);
if( null == node.statements )
{
node.statements = nf.statementList(null,init);
}
else
{
node.statements.items.add(0,init);
}
}
// Now turn the static names into definitions
// Generate code for the static property definitions
{
this_contexts.add(error_this);
cx.popScope(); // temporarily
for (Node n : node.staticfexprs)
{
n.evaluate(cx, this);
}
cx.pushScope(node.iframe);
this_contexts.removeLast();
}
fun_name_stack.removeLast();
max_params_stack.removeLast();
max_locals_stack.removeLast();
max_temps_stack.removeLast();
// Now evaluate each function expression
{
for (FunctionCommonNode n : node.fexprs)
{
n.evaluate(cx, this);
}
}
// Remove the top set of nested functions from the stack of sets
fexprs_sets.removeLast();
//ASSERT(fexprs_sets.size() == 0);
private_namespaces.pop_back();
default_namespaces.pop_back();
public_namespaces.pop_back();
protected_namespaces.pop_back();
static_protected_namespaces.pop_back();
usednamespaces_sets.pop_back();
used_def_namespaces_sets.pop_back();
importednames_sets.pop_back();
FinishClass(cx,node.cframe.builder.classname,null,false, false, false, node.cframe.is_nullable);
this_contexts.removeLast();
// pop the iframe now so we process class defs in static scope
cx.popScope(); // iframe
// Now evaluate each class definition
{
// node.clsdefs have the baseclass.cframe resolved, i.e. we've got fully-qualified class names.
// sort the class names based on "extends" and "implements"...
node.clsdefs = sortClassDefinitions(node.cx, node.clsdefs);
if (found_circular_or_duplicate_class_definition == false)
{
for (ClassDefinitionNode clsdef : node.clsdefs)
{
clsdef.evaluate(cx,this);
}
}
}
// Remove the top set of nested classes from the stack of sets
instanceinits_sets.removeLast();
staticfexprs_sets.removeLast();
cx.popStaticClassScopes(node);
node.debug_name = region_name_stack.back();
// store debug name on the slot as well. asDoc needs fully qualified debug_names for all
// type references.
Slot s = node.ref.getSlot(cx,GET_TOKEN);
if (s != null)
{
s.setDebugName(node.debug_name);
s.setConst(true); // class slots are const
}
region_name_stack.removeLast();
strict_context.pop_back();
//ASSERT(fexprs_sets.size() == 0);
interfaceMethods = lastInterfaceMethods;
}
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
default_namespaces.pop_back();
public_namespaces.pop_back();
usednamespaces_sets.pop_back();
used_def_namespaces_sets.pop_back();
importednames_sets.pop_back();
}
node.needs_init = true;
if (debug)
{
System.out.print("\n// -ClassDefinitionNode");
}
return node.ref;
}
public Value evaluate(Context cx, BinaryClassDefNode node)
{
if( resolveInheritance )
{
if (node.baseref != null)
{
Value val = node.baseref.getValue(cx);
if (val == null)
{
fa_unresolved_sets.last().add(node.baseref);
}
else
{
TypeValue type = ((val instanceof TypeValue) ? (TypeValue)val : null);
if (type == null)
{
// stay silent. we'll report this in the else part...
// cx.error(node.baseclass.pos(), kError_UnknownBaseClass);
}
else
if( type.builder.is_final )
{
// stay silent. we'll report this in the else part...
// cx.error(node.baseclass.pos(), kError_BaseClassIsFinal);
}
else
if ( type.builder instanceof ClassBuilder && ((ClassBuilder)type.builder).is_interface )
{
// stay silent. we'll report this in the else part...
// cx.error(node.baseclass.pos(), node.isInterface() ? kError_CannotExtendClass : kError_CannotExtendInterface);
}
else
{
inheritClassSlots(node.cframe, node.iframe, type, cx);
}
}
}
}
else
{
if( node.baseref != null )
{
TypeValue baseType = (TypeValue)(node.baseref.getValue(node.cx));
if( baseType != null)
{
// Copy the methods/vars/properties from the base class into the derived class
inheritClassSlots(node.cframe, node.cframe.prototype, baseType, node.cx);
}
}
if( node.interfaces != null )
{
ObjectList<ReferenceValue> interface_refs = ((InstanceBuilder)node.iframe.builder).interface_refs;
for(int i = 0; i < node.interfaces.size(); ++i)
{
Value v = node.interfaces.values.get(i);
if (v instanceof ReferenceValue)
{
ReferenceValue ref = (ReferenceValue)v;
if (ref.slot == null)
{
// if the slot is null, it's probably because we can't find
// the library with the parent class (e.g., playerglobal.swc)
// ... it's a fatal error, because we'd just throw a NullPointerException
// in the scanInterfaceMethods call below. (srj)
cx.error(node.pos(), kError_UnknownType, ref.toMultiName());
continue;
}
interface_refs.push_back(ref);
Value v2 = v.getValue(node.cx);
TypeValue t = ((v2 instanceof TypeValue) ? (TypeValue)v2 : null);
if (t != null && t.isInterface())
{
if (node instanceof BinaryInterfaceDefinitionNode)
{
// If this is an interface, inherit the super-interface slots.
inheritClassSlots(node.cframe, node.iframe, t, node.cx);
}
}
}
}
}
Slot s = node.ref.getSlot(node.cx,GET_TOKEN);
if (s != null)
{
s.setDebugName(node.debug_name);
s.setConst(true); // class slots are const
}
Names lastInterfaceMethods = interfaceMethods;
interfaceMethods = null;
scanInterfaceMethods(node.cx, node);
processInterfacePublicMethods(node.cx, node.iframe);
interfaceMethods = lastInterfaceMethods;
node.cframe.type = node.cx.typeType().getDefaultTypeInfo();
}
return null;
}
public Value evaluate(Context cx, BinaryInterfaceDefinitionNode node)
{
return this.evaluate(cx, (BinaryClassDefNode)node);
}
private void inheritClassSlots(TypeValue cframe, ObjectValue iframe, TypeValue baseType, Context ctx)
{
inheritClassSlotsStatic(cframe, iframe, baseType, ctx);
}
static void inheritClassSlotsStatic(TypeValue cframe, ObjectValue iframe, TypeValue baseType, Context ctx)
{
TypeValue superType = baseType;
cframe.baseclass = superType;
ObjectValue baseobj = baseType.prototype;
InstanceBuilder basebui = ((superType.prototype.builder instanceof InstanceBuilder) ? (InstanceBuilder)superType.prototype.builder : null);
InstanceBuilder bui = (InstanceBuilder)(iframe.builder);
bui.canEarlyBind = basebui.canEarlyBind;
bui.basebui = basebui;
bui.var_offset = basebui.var_offset + baseobj.var_count;
// ISSUE: this should be zero when the base class is outside the current abc file
bui.method_offset = basebui.method_offset + basebui.method_count;
TypeValue baseclass = cframe.baseclass;
ClassBuilder classbui = ((cframe.builder instanceof ClassBuilder) ? (ClassBuilder)cframe.builder : null);
classbui.basebui = (baseclass != null ? (baseclass.builder instanceof ClassBuilder ? (ClassBuilder)baseclass.builder : null) : null);
if( classbui.basebui != null && classbui.basebui.basebui != null)
{
classbui.basebui.is_intrinsic = classbui.basebui.basebui.is_intrinsic;
}
iframe.addBaseObj(baseobj);
if( !cframe.isInterface() && !baseType.isInterface() )
iframe.setProtectedNamespaces(((ClassBuilder)cframe.builder).protected_namespace, ((ClassBuilder)baseType.builder).protected_namespace);
/*
Names names = basebui.getNames();
if (names != null)
{
for (int i = 0; (i = names.hasNext(i)) != -1; i++)
{
String name = names.getName(i);
ObjectValue baseNamespace = names.getNamespace(i);
ObjectValue ns = baseNamespace;
if (baseNamespace.isPrivate() && baseNamespace != this.local_file_namespace)
{
// no point in inheriting private members from the base class
continue;
}
if (baseNamespace.isProtected())
{
// Inherit into the protected namespace of this class instead
ns = classbui.protected_namespace;
}
switch (names.getType(i))
{
case Names.METHOD_NAMES:
{
assert !Builder.removeBuilderNames;
// For each method:
// Allocate a new method id (matching the base method id)
// Create a binding to it
if (name.equals("$construct"))
{
break; // don't inherit constructors
}
int base_slot_id = baseobj.getSlotIndex(ctx,GET_TOKEN,name,baseNamespace);
int implied_id = baseobj.getImplicitIndex(ctx,base_slot_id,EMPTY_TOKEN);
Slot explicitSlot = baseobj.getSlot(ctx,base_slot_id);
Slot slot = baseobj.getSlot(ctx,implied_id);
bui.InheritCall(ctx, iframe, names.getName(i), ns, explicitSlot, slot);
break;
}
case Names.GET_NAMES:
{
int slot_id = baseobj.getSlotIndex(ctx,GET_TOKEN,name,baseNamespace);
int implicit_id = baseobj.getImplicitIndex(ctx,slot_id,EMPTY_TOKEN);
if( implicit_id != slot_id)
{
if(!Builder.removeBuilderNames)
break;
// its a getter for a method inherit the call
if (name.equals("$construct"))
{
break; // don't inherit constructors
}
Slot explicitSlot = baseobj.getSlot(ctx,slot_id);
Slot slot = baseobj.getSlot(ctx,implicit_id);
bui.InheritCall(ctx, iframe, name, ns, explicitSlot, slot);
}
else
{
Slot slot = baseobj.getSlot(ctx,slot_id);
if(slot instanceof VariableSlot) {
bui.InheritVar(ctx, iframe, name, ns, slot);
} else {
bui.InheritGet(ctx, iframe, name, ns, slot);
}
}
break;
}
case Names.SET_NAMES:
{
int slot_id = baseobj.getSlotIndex(ctx,SET_TOKEN,name,baseNamespace);
Slot slot = baseobj.getSlot(ctx,slot_id);
bui.InheritSet(ctx, iframe, name, ns, slot);
break;
}
case Names.VAR_NAMES:
{
assert !Builder.removeBuilderNames;
int slot_id = baseobj.getSlotIndex(ctx,GET_TOKEN,name,baseNamespace);
Slot inheritedSlot = baseobj.getSlot(ctx,slot_id);
bui.InheritVar(ctx, iframe, name, ns, inheritedSlot);
break;
}
}
}
}
*/
}
public Value evaluate(Context cx, InterfaceDefinitionNode node)
{
Value val = this.evaluate(cx, (ClassDefinitionNode) node);
ReferenceValue ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
Slot slot = ref!=null?ref.getSlot(cx):null;
if( slot == null )
{
cx.internalError(node.pos(), "internal error in FA::InterfaceDefinitionNode has no slot");
return null;
}
slot.setImplNode(node); // use this to validate class definitions during CE
((ClassBuilder)node.cframe.builder).is_interface = true;
// check state == Inheritance before checking. Don't want to log errors twice
if (node.attrs != null && node.state == ProgramNode.Inheritance)
{
if (node.attrs.hasFinal)
{
cx.error(node.pos(),kError_InvalidInterfaceAttribute, "final");
}
if (node.attrs.hasDynamic)
{
cx.error(node.pos(),kError_InvalidInterfaceAttribute, "dynamic");
}
if (node.attrs.hasNative)
{
cx.error(node.pos(),kError_InvalidInterfaceAttribute, "native");
}
if (node.attrs.hasPrivate)
{
cx.error(node.pos(),kError_InvalidInterfaceAttribute, PRIVATE);
}
if (node.attrs.hasProtected)
{
cx.error(node.pos(),kError_InvalidInterfaceAttribute, PROTECTED);
}
}
return null;
}
public Value evaluate(Context cx, ClassNameNode node)
{
Value val = null;
if (node.pkgname != null)
{
node.pkgname.evaluate(cx, this);
}
if (node.ident != null)
{
val = node.ident.evaluate(cx, this);
}
return val;
}
public Value evaluate(Context cx, InheritanceNode node)
{
if (node.baseclass != null)
{
node.baseclass.evaluate(cx, this);
}
if (node.interfaces != null)
{
node.interfaces.evaluate(cx, this);
}
return null;
}
public Value evaluate(Context cx, AttributeListNode node)
{
if( node.namespace_ids.size() != 0 || node.namespaces.size() != 0 )
{
return null;
}
ObjectValue obj = null;
ReferenceValue ref = null;
// Use these for keeping track of if we have already seen a given attribute.
// This is used for error checking (attributes should not appear more than once)
boolean setPrivate = false;
boolean setProtected = false;
boolean setPublic = false;
boolean setInternal = false;
boolean setDynamic = false;
boolean setVirtual = false;
boolean setFinal = false;
boolean setOverride = false;
boolean setStatic = false;
boolean setNative = false;
boolean setPrototype = false;
for (int i = 0, size = node.items.size(); i < size; i++)
{
Node n = node.items.get(i);
if( n != null )
{
if( n.hasAttribute(PRIVATE) )
{
if( setPrivate )
{
cx.error(n.pos(), kError_DuplicateAttribute, PRIVATE);
}
setPrivate = node.hasPrivate = true;
}
else
if( n.hasAttribute(PROTECTED) )
{
if( setProtected )
{
cx.error(n.pos(), kError_DuplicateAttribute, PROTECTED);
}
setProtected = node.hasProtected = true;
}
else
if( n.hasAttribute(PUBLIC) )
{
if( setPublic )
{
cx.error(n.pos(), kError_DuplicateAttribute, PUBLIC);
}
setPublic = node.hasPublic = true;
}
else
if( n.hasAttribute(INTERNAL) )
{
if( setInternal )
{
cx.error(n.pos(), kError_DuplicateAttribute, INTERNAL);
}
setInternal = node.hasInternal = true;
}
else
{
if( n.hasAttribute(PROTOTYPE) )
{
if( setPrototype )
{
cx.error(n.pos(), kError_DuplicateAttribute, PROTOTYPE);
}
setPrototype = node.hasPrototype = true;
}
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
Value val1 = n.evaluate(cx,this);
ns_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
ref = ((val1 instanceof ReferenceValue) ? (ReferenceValue)val1 : null);
if( ref != null )
{
Value val2 = ref.getValue(cx);
obj = ((val2 instanceof ObjectValue) ? (ObjectValue)val2 : null);
if( obj!=null )
{
if( obj == ObjectValue.intrinsicAttribute ) {
node.hasIntrinsic = true;
cx.error(n.pos(), kError_Unsupported_Intrinsic);
}
else
if( obj == ObjectValue.staticAttribute ) {
if( setStatic )
{
cx.error(n.pos(), kError_DuplicateAttribute, STATIC);
}
setStatic = node.hasStatic = true;
if( !(cx.scope().builder instanceof InstanceBuilder) && !(cx.scope().builder instanceof ClassBuilder))
{
cx.error(n.pos(), kError_InvalidStatic);
}
}
else
if( obj == ObjectValue.dynamicAttribute ) {
if( setDynamic )
{
cx.error(n.pos(), kError_DuplicateAttribute, "dynamic");
}
setDynamic = node.hasDynamic = true;
}
else
if( obj == ObjectValue.virtualAttribute ) {
if( setVirtual )
{
cx.error(n.pos(), kError_DuplicateAttribute, "virtual");
}
setVirtual = node.hasVirtual = true; // error if has final too
if( !(cx.scope().builder instanceof InstanceBuilder) && !(cx.scope().builder instanceof ClassBuilder) )
{
cx.error(n.pos(), kError_InvalidVirtual);
}
}
else
if( obj == ObjectValue.finalAttribute ) {
if( setFinal )
{
cx.error(n.pos(), kError_DuplicateAttribute, "final");
}
setFinal = node.hasFinal = true; // error if has virtual too
}
else
if( obj == ObjectValue.overrideAttribute ) {
if( setOverride )
{
cx.error(n.pos(), kError_DuplicateAttribute, "override");
}
setOverride = node.hasOverride = true;
if( !(cx.scope().builder instanceof InstanceBuilder) && !(cx.scope().builder instanceof ClassBuilder) )
{
cx.error(n.pos(), kError_InvalidOverride);
}
}
else
if( obj == ObjectValue.nativeAttribute ) {
if( setNative )
{
cx.error(n.pos(), kError_DuplicateAttribute, "native");
}
setNative = node.hasNative = true;
}
else
{
if( obj != null )
{
if( "false".equals(obj.name) )
{
node.hasFalse = true;
}
if( !(cx.scope().builder instanceof ClassBuilder || cx.scope().builder instanceof InstanceBuilder) )
{
cx.error(node.pos(), kError_InvalidNamespace);
}
else
{
node.namespaces.push_back(obj);
node.namespace_ids.push_back(ref.name);
}
}
}
}
else
{
if( !(cx.scope().builder instanceof ClassBuilder || cx.scope().builder instanceof InstanceBuilder) )
{
cx.error(node.pos(), kError_InvalidNamespace);
}
else
{
node.namespaces.push_back(cx.getUnresolvedNamespace(cx, node, ref));
node.namespace_ids.push_back(ref.name);
}
}
}
else
{
cx.error(node.pos(), kError_InvalidAttribute);
}
}
}
}
if (node.namespaces.size() != 0)
{
boolean foundUserNamespace = false;
for (ObjectValue ns : node.namespaces)
{
if (!(ns instanceof NamespaceValue))
{
// Error: Not a namespace attribute
cx.error(node.pos(), kError_InvalidAttribute);
}
else if (!foundUserNamespace)
{
foundUserNamespace = true;
node.setUserNamespace(ns);
}
else
{
cx.error(node.pos(), kError_MultipleNamespaceAttributes);
break;
}
}
}
// Only one of public, private, protected, internal may be used.
if (((node.hasPrivate?1:0) + (node.hasPublic?1:0) + (node.hasProtected?1:0) + (node.hasInternal?1:0)) > 1)
{
cx.error(node.pos(), kError_ConflictingAccessSpecifiers);
}
if (node.hasUserNamespace() && (node.hasPrivate || node.hasPublic || node.hasProtected || node.hasInternal))
{
cx.error(node.pos(), kError_NamespaceAccessSpecifiers);
}
return null;
}
public Value evaluate(Context cx, IncludeDirectiveNode node)
{
if( !node.in_this_include )
{
node.in_this_include = true;
node.prev_cx = new Context(cx.statics);
node.prev_cx.switchToContext(cx);
// DANGER: it may not be obvious that we are setting the
// the context of the outer statementlistnode here
cx.switchToContext(node.cx);
}
else
{
node.in_this_include = false;
cx.switchToContext(node.prev_cx); // restore prevailing context
node.prev_cx = null;
}
return null;
}
public Value evaluate(Context cx, ImportNode node)
{
ObjectValue baseobj = node.program.frame;
ObjectValue obj = cx.scope();
String id = node.filespec.value;
QName qname = new QName(cx.publicNamespace(), id);
ImportBuilder bui = new ImportBuilder(qname);
inheritSlots(baseobj, obj, bui, cx);
return null;
}
public static void inheritSlots(ObjectValue baseobj, ObjectValue obj, Builder bui, Context cx)
{
inheritSlots(baseobj, obj, bui, cx, false);
}
public static void inheritContextSlots(ObjectValue baseobj, ObjectValue obj, Builder bui, Context cx)
{
inheritSlots(baseobj, obj, bui, cx, true);
}
public static void inheritSlots(ObjectValue baseobj, ObjectValue obj, Builder bui, Context cx, boolean limitToContext)
{
Builder basebui = (baseobj.builder);
Names names = basebui.getNames();
if (names != null)
{
for (int i = 0; (i = names.hasNext(i)) != -1; i++)
{
String name = names.getName(i);
ObjectValue namespace = names.getNamespace(i);
switch (names.getType(i))
{
case Names.METHOD_NAMES:
{
assert !Builder.removeBuilderNames;
int inherited_get_id = baseobj.getSlotIndex(cx,GET_TOKEN,name,namespace);
int inherited_call_id = baseobj.getImplicitIndex(cx,inherited_get_id,EMPTY_TOKEN);
Slot inheritedSlot = baseobj.getSlot(cx,inherited_get_id);
Slot inheritedCallSlot = baseobj.getSlot(cx,inherited_call_id);
bui.InheritCall(cx,obj,name,namespace,inheritedSlot,inheritedCallSlot);
break;
}
case Names.GET_NAMES:
{
int slot_id = baseobj.getSlotIndex(cx,GET_TOKEN,name,namespace);
Slot slot = baseobj.getSlot(cx,slot_id);
if(slot == null || (limitToContext && slot.declaredBy != null && slot.declaredBy.builder.contextId != basebui.contextId))
break;
int implicit_id = baseobj.getImplicitIndex(cx,slot_id,EMPTY_TOKEN);
if(Builder.removeBuilderNames && slot_id != implicit_id && slot instanceof MethodSlot)
{
Slot inheritedCallSlot = baseobj.getSlot(cx,implicit_id);
bui.InheritCall(cx,obj,name,namespace,slot,inheritedCallSlot);
break;
}
if(Builder.removeBuilderNames && slot instanceof VariableSlot)
{
// hack propagated from VAR_NAMES case
// Hack
slot.setVarIndex(-1);
int index = slot.implies(cx,EMPTY_TOKEN);
Slot inheritedCallSlot = baseobj.getSlot(cx,index);
index = slot.implies(cx,NEW_TOKEN);
Slot inheritedConstructorSlot = baseobj.getSlot(cx,index);
bui.InheritVar(cx, obj, name, namespace, slot);
if (inheritedCallSlot != null)
{
obj.addSlot(inheritedCallSlot);
}
if (inheritedConstructorSlot != null)
{
obj.addSlot(inheritedConstructorSlot);
}
}
// skip non-public names, this isn't applied to methods so its down here
if(namespace != cx.publicNamespace())
{
break;
}
bui.InheritGet(cx,obj,name,namespace,slot);
break;
}
case Names.SET_NAMES:
{
// skip non-public names
if(namespace != cx.publicNamespace())
{
break;
}
int slot_id = baseobj.getSlotIndex(cx,SET_TOKEN,name,namespace);
Slot slot = baseobj.getSlot(cx,slot_id);
bui.InheritSet(cx,obj,name,namespace,slot);
break;
}
case Names.VAR_NAMES:
{
assert !Builder.removeBuilderNames;
int slot_id = baseobj.getSlotIndex(cx,VAR_TOKEN,name,namespace);
Slot inheritedSlot = baseobj.getSlot(cx,slot_id);
// Hack
inheritedSlot.setVarIndex(-1);
int index = inheritedSlot.implies(cx,EMPTY_TOKEN);
Slot inheritedCallSlot = baseobj.getSlot(cx,index);
index = inheritedSlot.implies(cx,NEW_TOKEN);
Slot inheritedConstructorSlot = baseobj.getSlot(cx,index);
bui.InheritVar(cx, obj, name, namespace, inheritedSlot);
if (inheritedCallSlot != null)
{
obj.addSlot(inheritedCallSlot);
}
if (inheritedConstructorSlot != null)
{
obj.addSlot(inheritedConstructorSlot);
}
}
}
}
/*
if (obj.baseMethodNames == null)
{
obj.baseMethodNames = new Names();
}
obj.baseMethodNames.putAll(names, Names.METHOD_NAMES);
*/
}
}
public Value evaluate( Context unused_cx, ImportDirectiveNode node )
{
Context cx = node.cx;
if( node.name != null )
{
Value val = node.name.evaluate(cx,this);
ReferenceValue ref = ((val instanceof ReferenceValue) ? (ReferenceValue)val : null);
if( ref != null)
{
if( !( cx.isNamespace(ref.name) && cx.getNamespace(ref.name).isPackage() ) )
{
package_unresolved_sets.last().add(ref);
}
String pkg_name = ref.name;
String def_name = node.name.id.def_part;
if( def_name.length() > 0 )
{
Multinames importednames;
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
importednames = node.pkgdef.imported_names;
}
else
{
importednames = importednames_sets.back();
}
Namespaces nss = importednames.get(def_name);
// If there is one, then get the qualifier map for that name.
// Otherwise, create a qualifier map and a new property.
if( nss == null )
{
importednames.put(def_name,new Namespaces());
}
// Add the qualifier to the qualifiers map, and set its value to index.
Namespaces prop = importednames.get(def_name);
if( !prop.contains(cx.getNamespace(pkg_name)) )
{
prop.push_back(cx.getNamespace(pkg_name));
import_def_unresolved_sets.last().add(new ReferenceValue(cx, null, def_name, cx.getNamespace(pkg_name)));
}
}
else
{
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
node.pkgdef.used_namespaces.push_back(cx.getNamespace(pkg_name));
}
else
{
usednamespaces_sets.back().push_back(cx.getNamespace(pkg_name));
}
}
}
else
{
cx.error(node.name.pos(), kError_Unknown_Namespace);
}
}
return null;
}
public Value evaluate(Context cx, SuperExpressionNode node)
{
switch( this_contexts.last() )
{
case global_this:
case error_this:
cx.error(node.pos(),kError_InvalidSuperExpression);
break;
default:
// valid use of this
break;
}
if( node.expr != null )
{
node.expr.evaluate(cx,this);
}
if( super_context.last() == super_statement )
{
super_context.set(super_context.size()-1, super_error2);
}
return null;
}
public Value evaluate(Context cx, SuperStatementNode node)
{
switch( super_context.last() )
{
case super_statement:
int index = cx.getScopes().size()-2;
ObjectValue obj = cx.scope(index);
InstanceBuilder bui = ((obj.builder instanceof InstanceBuilder) ? (InstanceBuilder)obj.builder : null);
if( bui == null) cx.internalError("internal error: super statement outside of instance constructor");
else bui.calls_super_ctor = true;
if( node.call.args != null )
{
node.call.args.evaluate(cx,this);
}
TypeValue type = obj.type.getTypeValue();
TypeValue basecls = type.baseclass;
if( basecls != null )
{
node.baseobj = basecls.prototype;
}
else
{
node.baseobj = cx.noType().prototype;
}
super_context.set(super_context.size()-1, super_error2);
break;
case super_error2:
cx.error(node.pos(),kError_IllegalSuperStatement);
break;
case super_error_es4:
cx.error(node.pos(), kError_InvalidES4SuperStatement);
break;
case super_error:
default:
cx.error(node.pos(),kError_InvalidSuperStatement);
break;
}
return null;
}
public Value evaluate( Context cx, ConfigNamespaceDefinitionNode node )
{
// TODO: something to ensure that other definitions don't shadow the
// TODO: config namespace.
return null;
}
public Value evaluate( Context cx, NamespaceDefinitionNode node )
{
// first time we are evaluated, we create a var for the namespace var and possibly mark it as const.
// If so, we mark needs_init = true.
if (node.needs_init /* && doing_method() */)
{
// second time we are evaluated from statementList's evaluator in order to add this def to
// the block's def_bits.
getEmitter().AddStmtToBlock(node.toString());
node.gen_bits = getEmitter().NewDef(node);
Slot slot = node.ref.getSlot(cx);
if (slot != null)
slot.addDefBits(node.gen_bits);
node.needs_init = false;
return null;
}
if (node.ref != null) { return null; }
// If this is a toplevel definition (pkgdef!=null), then set up access namespaces
if( node.attrs != null && node.attrs.hasAttribute(STATIC) )
{
cx.error(node.attrs.pos(), kError_StaticModifiedNamespace);
}
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.push_back(node.pkgdef.publicNamespace);
default_namespaces.push_back(node.pkgdef.internalNamespace);
usednamespaces_sets.push_back(node.pkgdef.used_namespaces);
importednames_sets.push_back(node.pkgdef.imported_names);
}
ClassBuilder classBuilder = classBuilderOnScopeChain(cx);
if (classBuilder != null && classBuilder.is_interface)
{
cx.error(node.pos(), kError_NamespaceInInterface);
}
Namespaces namespaces = new Namespaces();
ObjectList<String> namespace_ids = new ObjectList<String>();
computeNamespaces(cx,node.attrs,namespaces,namespace_ids);
Value v = node.name.evaluate(cx,this);
node.ref = ((v instanceof ReferenceValue) ? (ReferenceValue)v : null);
// Get the current object and its builder
ObjectValue obj = cx.scope();
Builder bui = obj.builder;
boolean is_intrinsic = false;
int slot_id = -1;
Namespaces hasNamespaces = obj.hasNames(cx,GET_TOKEN,node.ref.name,namespaces);
if( hasNamespaces != null )
{
cx.error(node.pos(), kError_DuplicateNamespaceDefinition);
}
else
{
if( bui.is_intrinsic || is_intrinsic )
{
slot_id = bui.ExplicitVar(cx,obj,node.ref.name,namespaces,cx.noType(),-1);
}
else
{
int var_id;
var_id = bui.Variable(cx,obj);
slot_id = bui.ExplicitVar(cx,obj,node.ref.name,namespaces,cx.noType(),-1,-1,var_id);
}
}
node.debug_name = region_name_stack.back();
// store debug name on the slot as well. asDoc needs fully qualified debug_names for all
// type references.
Slot s = obj.getSlot(cx,slot_id);
if (s != null)
{
s.setDebugName(node.debug_name);
}
node.qualifiedname = cx.computeQualifiedName(node.debug_name, node.ref.name, namespaces.back(), EMPTY_TOKEN);
if( !is_intrinsic && slot_id >= 0 )
{
Slot slot = obj.getSlot(cx,slot_id);
if( node.value != null )
{
if( node.value instanceof LiteralStringNode )
{
// NOTE we distinguish between package public (NS_PUBLIC) and explicit namespaces (NS_EXPLICIT)
// so that we know which builtin namespaces to mark with a version marker
slot.setObjectValue(cx.getNamespace(((LiteralStringNode)node.value).value, Context.NS_EXPLICIT));
}
else
{
Value val= node.value.evaluate(cx, this);
if( val instanceof ReferenceValue )
{
Slot ns_slot = ((ReferenceValue)val).getSlot(cx);
if( ns_slot != null && cx.isNamespace(ns_slot.getObjectValue()) )
{
slot.setObjectValue(ns_slot.getObjectValue() );
}
}
}
}
else
{
String name = cx.debugName(region_name_stack.back(),node.ref.name,namespace_ids,EMPTY_TOKEN);
ObjectValue ns = cx.getNamespace(name.intern(),Context.NS_INTERNAL);
slot.setObjectValue(ns);
}
// must set slot to const before we can get its ObjectValue. VariableSlots only return ObjectValues
// when they are const.
slot.setConst(true);
if( slot.getObjectValue() == null )
{
cx.error(node.value.pos(), kError_InvalidNamespaceInitializer);
}
node.needs_init = true; // need to re-evaluate to get this definition into the def_bits for the block
}
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.pop_back();
default_namespaces.pop_back();
usednamespaces_sets.pop_back();
importednames_sets.pop_back();
}
return null;
}
public Value evaluate( Context cx, UseDirectiveNode node )
{
if (node.ref != null) { return null; }
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.push_back(node.pkgdef.publicNamespace);
default_namespaces.push_back(node.pkgdef.internalNamespace);
usednamespaces_sets.push_back(node.pkgdef.used_namespaces);
importednames_sets.push_back(node.pkgdef.imported_names);
}
ObjectValue obj = null;
if( node.expr != null )
{
rt_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
node.ref = (ReferenceValue)node.expr.evaluate(cx,this);
Value value = node.ref.getValue(cx);
obj = ((value instanceof ObjectValue) ? (ObjectValue) value : null);
ns_unresolved_sets.last().addAll(unresolved);
unresolved.clear();
}
if( obj != null )
{
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
node.pkgdef.used_namespaces.push_back(obj);
}
else
{
usednamespaces_sets.back().push_back(obj);
}
}
else
{
ObjectValue surrogate = cx.getUnresolvedNamespace(cx, node, node.ref);
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
node.pkgdef.used_namespaces.push_back(surrogate);
}
else
{
usednamespaces_sets.back().push_back(surrogate);
}
}
if( node.pkgdef != null && cx.getScopes().size() == 1 )
{
public_namespaces.pop_back();
default_namespaces.pop_back();
usednamespaces_sets.pop_back();
importednames_sets.pop_back();
}
return null;
}
public Value evaluate(Context cx, RestExpressionNode node)
{
cx.internalError(node.pos(), "RestExpressionNode not yet implemented");
return null;
}
public Value evaluate(Context cx, ErrorNode node)
{
if (!errorNodeSeen)
{
errorNodeSeen = true;
cx.error(node.pos(), node.errorCode, node.errorArg);
}
return null;
}
public Value evaluate(Context cx, PragmaNode node)
{
if (debug)
{
System.out.print("\n// +PragmaNode");
}
if (node.list != null)
node.list.evaluate(cx, this);
if (debug)
{
System.out.print("\n// -PragmaNode");
}
return null;
}
public Value evaluate(Context cx, UsePrecisionNode node)
{
if (debug)
{
System.out.print("\n// +UsePrecisionNode");
}
NumberUsage currentParams = number_usage_stack.last();
if ((1 <= node.precision) && (node.precision <= 34)) {
currentParams.set_precision(node.precision);
}
else {
cx.error(node.pos(), kError_InvalidPrecision);
}
if (debug)
{
System.out.print("\n// -UsePrecisionNode");
}
return null;
}
public Value evaluate(Context cx, UseNumericNode node)
{
if (debug)
{
System.out.print("\n// +UseNumericNode");
}
NumberUsage currentParams = number_usage_stack.last();
currentParams.set_usage(node.numeric_mode);
if (debug)
{
System.out.print("\n// -UseNumericNode");
}
return null;
}
public Value evaluate(Context cx, UseRoundingNode node)
{
if (debug)
{
System.out.print("\n// +UseRoundingNode");
}
NumberUsage currentParams = number_usage_stack.last();
currentParams.set_rounding(node.mode);
if (debug)
{
System.out.print("\n// -UseRoundingNode");
}
return null;
}
public Value evaluate(Context cx, PragmaExpressionNode node)
{
cx.internalError(node.pos(), "PragmaExpressionNode not yet implemented");
return null;
}
public Value evaluate(Context cx, TypedIdentifierNode node)
{
cx.internalError(node.pos(), "TypedIdentifierNode not yet implemented");
return null;
}
public Value evaluate(Context cx, UntypedVariableBindingNode node)
{
cx.internalError(node.pos(), "UntypedVariableBindingNode not yet implemented");
return null;
}
public Value evaluate(Context cx, ParenListExpressionNode node)
{
cx.internalError(node.pos(), "ParenListExpressionNode not yet implemented");
return null;
}
public Value evaluate(Context cx, ParenExpressionNode node)
{
cx.internalError(node.pos(), "ParenExpressionNode not yet implemented");
return null;
}
public Value evaluate(Context cx, LiteralXMLNode node)
{
if (debug)
{
System.out.print("\n// +LiteralXML");
}
if (node.list != null)
{
node.list.evaluate(cx, this);
}
getEmitter().AddStmtToBlock(node.toString());
if (debug)
{
System.out.print("\n// -LiteralXML");
}
return null;
}
public Value evaluate(Context cx, MetaDataNode node)
{
return null;
}
public Value evaluate(Context cx, EmptyElementNode node)
{
return null;
}
public Value evaluate(Context cx, DefaultXMLNamespaceNode node)
{
if( node.expr != null )
{
node.expr.evaluate(cx,this);
}
return null;
}
public Value evaluate(Context cx, DocCommentNode node)
{
// do nothing
return null;
}
private ClassBuilder classBuilderOnScopeChain(Context cx)
{
for (int i = cx.getScopes().size(); --i >= 0; )
{
Builder bui = cx.scope(i).builder;
if (bui instanceof ClassBuilder)
{
return (ClassBuilder)bui;
}
}
return null;
}
/* This method works its way up the scope chain and determines if any of the
* scope builders going up to the global scope are instances of ClassBuilder
* Useful when determining if we are currently, or at any previous point,
* within a class definition, due to nesting.
*/
private <T extends Builder>
boolean currentScopeChainContainsBuilder
(Class<T> builder, Context cx, int startingScopeDepth)
{
if(startingScopeDepth < 0) return false;
return (builder.isInstance(cx.scope(startingScopeDepth).builder)
|| currentScopeChainContainsBuilder(builder, cx,startingScopeDepth-1));
}
private void processInterfacePublicMethods(Context cx, ObjectValue iframe)
{
// The interface method names map says which interface public methods are out there.
// For each interface public method:
// - Do we have a public method by that name in this class?
// - If so, take the list of interfaces that have that method name
// - Scan the class, removing interfaces that actually were implemented
// - Now add slots pointing to the public method for the remaining namespaces
InstanceBuilder ibui = (InstanceBuilder)iframe.builder;
if (interfaceMethods == null)
{
return;
}
for (int i = 0; (i = interfaceMethods.hasNext(i)) != -1; i++)
{
String name = interfaceMethods.getName(i);
int type = interfaceMethods.getType(i);
ObjectValue interfaceNamespace = interfaceMethods.getNamespace(i);
int kind = EMPTY_TOKEN;
if (type == Names.GET_NAMES)
{
kind = GET_TOKEN;
}
else if (type == Names.SET_NAMES)
{
kind = SET_TOKEN;
}
int slot_id = iframe.getSlotIndex(cx, (kind == EMPTY_TOKEN) ? GET_TOKEN : kind, name, cx.publicNamespace());
if (slot_id <= 0)
{
continue;
}
int implied_id = iframe.getImplicitIndex(cx,slot_id,EMPTY_TOKEN);
//Names names = ibui.getNames();
if (ibui.objectValue.hasName(cx, kind, name, interfaceNamespace))
{
continue;
}
switch (interfaceMethods.getType(i))
{
case Names.GET_NAMES:
iframe.defineName(cx, GET_TOKEN, name, interfaceNamespace, slot_id);
ibui.Name(cx, GET_TOKEN, name, interfaceNamespace);
break;
case Names.SET_NAMES:
iframe.defineName(cx, SET_TOKEN, name, interfaceNamespace, slot_id);
ibui.Name(cx, SET_TOKEN, name, interfaceNamespace);
break;
case Names.METHOD_NAMES:
assert !Builder.removeBuilderNames;
iframe.defineName(cx, GET_TOKEN, name, interfaceNamespace, slot_id);
ibui.Name(cx, GET_TOKEN, name, interfaceNamespace);
iframe.defineName(cx, EMPTY_TOKEN, name, interfaceNamespace, implied_id);
ibui.Name(cx, EMPTY_TOKEN, name, interfaceNamespace);
break;
}
}
}
private void scanInterfaceMethods(Context cx, ClassDefinitionNode node)
{
ObjectValue iframe = node.iframe;
InterfaceWalker interfaceWalker = new InterfaceWalker(iframe);
if (interfaceWalker.hasNext())
{
interfaceMethods = new Names();
}
while (interfaceWalker.hasNext())
{
ObjectValue interfaceIFrame = interfaceWalker.next();
TypeValue interfaceCFrame = interfaceIFrame.type.getTypeValue();
// Add interface to used definition namespaces for this class.
// (Otherwise, hasNamespaces will not find interface qualifiers.)
node.used_def_namespaces.push_back(interfaceCFrame);
InstanceBuilder interfaceBuilder = (InstanceBuilder)interfaceIFrame.builder;
// Copy any public names into iframe's interfaceMethodNames table
Names names = interfaceBuilder.getNames();
if (names != null)
{
for (int i = 0; (i = names.hasNext(i)) != -1; i++)
{
if (names.getNamespace(i).compareTo(interfaceCFrame) != 0)
{
// public auto-magic only happens on methods in the interface
// namespace
continue;
}
String name = names.getName(i);
if(Builder.removeBuilderNames)
{
assert names.getType(i) != Names.METHOD_NAMES;
interfaceMethods.put(name, interfaceCFrame, names.getType(i), 0);
}
else
{
switch (names.getType(i))
{
case Names.METHOD_NAMES:
if (!name.equals("$construct"))
{
interfaceMethods.put(name, interfaceCFrame, Names.METHOD_NAMES, 0);
}
break;
case Names.GET_NAMES:
if (!names.containsKey(name, Names.METHOD_NAMES))
{
interfaceMethods.put(name, interfaceCFrame, Names.GET_NAMES, 0);
}
break;
case Names.SET_NAMES:
interfaceMethods.put(name, interfaceCFrame, Names.SET_NAMES, 0);
break;
}
}
}
}
}
}
// While brute force slow, this method is only used when reporting an error for a conflicting definition
String getFullNameForInheritedSlot(Context cx, ObjectValue declaredBy, String name)
{
String fullname = name;
fullname = declaredBy.type.getTypeValue().name.toString();
fullname += ".";
fullname += name;
/*
// find slot for original definition
while(inheritedSlot.inheritedSlot != null)
inheritedSlot = inheritedSlot.inheritedSlot;
// walk up scope chain looking for class cframes
for(int x=cx.getScopes.size()-1; x > -1; --x)
{
TypeValue classType = (cx.scope(x) instanceof TypeValue) ? (TypeValue)(cx.scope(x)) : null;
if (classType != null)
{
// now that we've found a class type in the scope, walk it's prototype's slots
// looking for the original definition
ObjectValue decl_obj = classType.prototype;
Slots slots = decl_obj.slots;
Iterator<Slot> it = slots.iterator();
for (int y = slots.size()-1; y > -1 && it.hasNext(); --y)
{
Slot slot = it.next();
if (slot == inheritedSlot)
{
fullname = decl_obj.type.name.toString();
fullname += ".";
fullname += name;
y = 0;
x = 0;
}
}
}
}
*/
return fullname;
}
private boolean namespacesContains(Context cx, Namespaces outer, Namespaces inner)
{
HashSet<ObjectValue> set = new HashSet<ObjectValue>();
for (ObjectValue ns : outer)
{
set.add(ns);
}
for (ObjectValue ns : inner)
{
if (!set.contains(ns))
{
return false;
}
}
return true;
}
private void adjustProtectedNamespace(Context cx, ReferenceValue ref)
{
if (ref != null)
{
ClassBuilder classBuilder = classBuilderOnScopeChain(cx);
if (classBuilder != null && classBuilder.basebui != null)
{
Namespaces namespaces = ref.getImmutableNamespaces();
if (namespaces.contains(classBuilder.protected_namespace))
{
namespaces = new Namespaces(namespaces);
namespaces.remove(classBuilder.protected_namespace);
namespaces.add(classBuilder.basebui.protected_namespace);
ref.setImmutableNamespaces(cx.statics.internNamespaces.intern(namespaces));
}
}
}
}
private ObjectValue getVariableDefinitionScope(Context cx)
{
for (int i=cx.getScopes().size(); --i >= 0; )
{
ObjectValue scope = cx.scope(i);
if (scope.builder.hasRegisterOffset())
{
return scope;
}
}
return null;
}
public Value evaluate(Context cx, TypeExpressionNode node)
{
Value v = node.expr.evaluate(cx, this);
if( v instanceof ReferenceValue )
{
((ReferenceValue)v).setNullableAnnotation(node.nullable_annotation, node.is_nullable);
}
return v;
}
}