blob: ebf89986ec2c329b69185836f5079044c36d1900 [file] [log] [blame]
/*
*
* 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 org.apache.flex.compiler.internal.as.codegen;
import static org.apache.flex.abc.ABCConstants.*;
import org.apache.flex.abc.ABCConstants;
import org.apache.flex.abc.visitors.IABCVisitor;
import org.apache.flex.abc.visitors.ITraitVisitor;
import org.apache.flex.abc.visitors.ITraitsVisitor;
import org.apache.flex.abc.semantics.MethodInfo;
import org.apache.flex.abc.semantics.Name;
import org.apache.flex.abc.semantics.Namespace;
import org.apache.flex.abc.semantics.Trait;
import org.apache.flex.abc.instructionlist.InstructionList;
import org.apache.flex.compiler.common.ASModifier;
import org.apache.flex.compiler.internal.as.codegen.ICodeGenerator.IConstantValue;
import org.apache.flex.compiler.internal.definitions.ClassDefinition;
import org.apache.flex.compiler.internal.definitions.DefinitionBase;
import org.apache.flex.compiler.internal.definitions.FunctionDefinition;
import org.apache.flex.compiler.internal.definitions.NamespaceDefinition;
import org.apache.flex.compiler.internal.semantics.SemanticUtils;
import org.apache.flex.compiler.internal.tree.as.ClassNode;
import org.apache.flex.compiler.internal.tree.as.FunctionNode;
import org.apache.flex.compiler.internal.tree.as.NamespaceNode;
import org.apache.flex.compiler.internal.tree.as.VariableNode;
import org.apache.flex.compiler.problems.StaticNamespaceDefinitionProblem;
import org.apache.flex.compiler.projects.ICompilerProject;
import org.apache.flex.compiler.tree.as.IASNode;
import org.apache.flex.compiler.tree.as.ICommonClassNode;
/**
* A ClassDirectiveProcessor generates an ABC class from a ClassNode and its
* contents. JSClassDirectiveProcessor is derived from ClassDirectiveProcessor
* and adds workarounds necessary for FalconJS. Ideally FalconJS should use
* ClassDirectiveProcessor and retire JSClassDirectiveProcessor. This
* implementation is part of FalconJS. For more details on FalconJS see
* org.apache.flex.compiler.JSDriver.
*/
@SuppressWarnings("nls")
public class JSClassDirectiveProcessor extends ClassDirectiveProcessor
{
JSGenerator m_generator;
/**
* Instructions to place in the class' initializer. Note that these are part
* of the class itself, as opposed to the above instructions which create
* the class at the global scope.
*/
InstructionList looseInsns = new InstructionList();
/**
* Constructor. Initializes the ClassDirectiveProcessor and its associated
* AET data structures.
*
* @param c - the class' AST.
* @param enclosing_scope - the immediately enclosing lexical scope.
* @param emitter - the active ABC emitter.
*/
public JSClassDirectiveProcessor(JSGenerator generator, ClassNode c, LexicalScope enclosing_scope, IABCVisitor emitter)
{
this(generator, c, addDefinition(enclosing_scope.getProject(), c.getDefinition()), enclosing_scope, emitter);
}
private static ClassDefinition addDefinition(ICompilerProject project, ClassDefinition cdef)
{
JSSharedData.instance.registerDefinition(cdef);
return cdef;
}
/**
* Constructor. Initializes the ClassDirectiveProcessor and its associated
* AET data structures.
*
* @param class_definition - the class' definition
* @param enclosing_scope - the immediately enclosing lexical scope.
* @param emitter - the active ABC emitter.
*/
public JSClassDirectiveProcessor(JSGenerator generator, IASNode node, ClassDefinition class_definition, LexicalScope enclosing_scope, IABCVisitor emitter)
{
super((ICommonClassNode)node, class_definition, enclosing_scope, emitter);
m_generator = generator;
/*
* // add explicit dependencies. final ICompilerProject project =
* classScope.getProject(); final ClassDefinition superclassDefinition =
* class_definition.resolveBaseClass( new ASDefinitionCache(project),
* new RecursionGuard(), classScope.getProblems()); if(
* superclassDefinition != null )
* JSSharedData.instance.addDependency(class_definition,
* superclassDefinition);
*/
generator.getReducer().setClassDefinition(enclosing_scope.getProject(), class_definition);
}
/**
* Finish the class' definition.
*/
@Override
void finishClassDefinition()
{
// Create the class' constructor function.
if (this.ctorFunction != null /* || !iinitInsns.isEmpty() */)
{
MethodInfo mi = m_generator.generateFunction(this.ctorFunction, classScope, this.iinitInsns);
if (mi != null)
this.iinfo.iInit = mi;
this.ctorFunction = null;
this.iinitInsns = new InstructionList();
}
// clear cinitInsns if there are no side effects
// by initializing the static members directly.
final String fullName = JSGeneratingReducer.createFullNameFromDefinition(classScope.getProject(), classDefinition);
if (!JSSharedData.instance.hasClassInit(fullName))
cinitInsns = new InstructionList();
// support for class inits
// loose statement are now collected in looseInsns
if (!this.looseInsns.isEmpty())
cinitInsns.addAll(looseInsns);
// base class injects ABC if not empty and then NPEs.
// save our insns and give it an empty list then restore
InstructionList cinitHack = cinitInsns;
cinitInsns = new InstructionList();
super.finishClassDefinition();
cinitInsns = cinitHack;
m_generator.getReducer().setClassDefinition(null, null);
}
/**
* Declare a function. TODO: static vs. instance.
*/
@Override
void declareFunction(FunctionNode func)
{
func.parseFunctionBody(classScope.getProblems());
boolean is_constructor = func.isConstructor();
functionSemanticChecks(func);
// Save the constructor function until
// we've seen all the instance variables
// that might need initialization.
if (is_constructor)
{
this.ctorFunction = func;
}
else
{
MethodInfo mi = m_generator.generateFunction(func, classScope, null);
ITraitVisitor tv;
if (mi != null)
{
FunctionDefinition funcDef = func.getDefinition();
Name funcName = funcDef.getMName(classScope.getProject());
if (func.hasModifier(ASModifier.STATIC))
tv = ctraits.visitMethodTrait(functionTraitKind(func, TRAIT_Method), funcName, 0, mi);
else
{
tv = itraits.visitMethodTrait(functionTraitKind(func, TRAIT_Method), funcName, 0, mi);
if (funcDef.getNamespaceReference() instanceof NamespaceDefinition.IProtectedNamespaceDefinition)
this.iinfo.flags |= ABCConstants.CLASS_FLAG_protected;
}
this.classScope.processMetadata(tv, funcDef.getAllMetaTags());
if (func.hasModifier(ASModifier.FINAL))
tv.visitAttribute(Trait.TRAIT_FINAL, Boolean.TRUE);
if (func.hasModifier(ASModifier.OVERRIDE))
tv.visitAttribute(Trait.TRAIT_OVERRIDE, Boolean.TRUE);
}
}
}
/**
* Declare a variable. TODO: static vs. instance.
*/
@Override
void declareVariable(VariableNode var)
{
verifyVariableModifiers(var);
DefinitionBase varDef = var.getDefinition();
m_generator.getReducer().startDeclareVariable(varDef);
boolean is_static = var.hasModifier(ASModifier.STATIC);
boolean is_const = SemanticUtils.isConst(var, classScope.getProject());
// simple initializers for public/protected vars go right on prototype.
// the rest (all private vars), all "complex" initializers (like array) get
// initialized in the constructor
boolean needs_constructor_init = true;
// generateConstantValue() returns null if no constant value
// can be generated, and null is the correct value for "no value."
IConstantValue constantValue = m_generator.generateConstantValue(var.getAssignedValueNode(), this.classScope.getProject());
// initializer is null if no constant value
// can be generated, and null is the correct value for "no value."
Object initializer = constantValue != null ? constantValue.getValue() : null;
ITraitVisitor tv = declareVariable(var, varDef, is_static, is_const, initializer);
this.classScope.processMetadata(tv, varDef.getAllMetaTags());
// Generate variable initializers and append them to the
// proper initialization list.
if (var.getChildCount() > 1)
{
// We need to put the correct traits visitor on the class'
// LexicalScope; the BURM may encounter variable declarations
// chained onto this one, and it will need the traits visitor to declare them.
// Save the scope's current traits visitor (which should be null)
// and restore it
ITraitsVisitor saved_traits_visitor = this.classScope.traitsVisitor;
assert (saved_traits_visitor == null);
try
{
// the following line causes duplicate Traits.
// JSEmitter::emitTraits works around duplicate Traits by checking against
// a visitedTraits set.
this.classScope.traitsVisitor = (is_static) ? ctraits : itraits;
this.classScope.resetDebugInfo();
InstructionList init_expression = m_generator.generateInstructions(var, CmcJSEmitter.__statement_NT, this.classScope);
if (init_expression != null && !init_expression.isEmpty())
{
// final JSEmitter emitter = (JSEmitter)this.classScope.getEmitter();
final String str = JSGeneratingReducer.instructionListToString(init_expression, true);
if (str.contains(" = "))
{
final String varInit = m_generator.getReducer().getVariableInitializer(varDef);
if (varInit != null && !varInit.isEmpty())
{
// set the value of the slot trait.
final String varName = varDef.getBaseName();
for (Trait t : this.classScope.traitsVisitor.getTraits())
{
final byte kind = t.getKind();
if (kind == TRAIT_Const || kind == TRAIT_Var)
{
boolean is_private = false;
final Name name = t.getNameAttr(Trait.TRAIT_NAME);
Namespace ns = name.getSingleQualifier();
if (ns.getKind() == CONSTANT_PrivateNs)
is_private = true;
if (name.getBaseName().equals(varName))
{
t.setAttr(Trait.SLOT_VALUE, varInit);
if (!is_private)
needs_constructor_init = false;
break;
}
}
}
}
if (is_static)
{
// see finishClassDefinition.
// We clear cinitInsns only if there are no side effects
// by initializing the static members directly.
// If varInit is null, or varInit is isEmpty()
// then we have side effects.
if (!init_expression.isEmpty())
registerClassInit(var);
cinitInsns.addAll(init_expression);
}
else if (needs_constructor_init)
iinitInsns.addAll(init_expression);
}
}
}
finally
{
this.classScope.traitsVisitor = saved_traits_visitor;
}
}
m_generator.getReducer().endDeclareVariable(varDef);
}
/**
* Ignore modifier nodes that are in the AST, but processed as attributes of
* the definition nodes. Other loose directives are processed as statements
* and added to the class' static init method.
*/
@Override
void processDirective(IASNode n)
{
switch (n.getNodeID())
{
case StaticID:
case FinalID:
case OverrideID:
case UseID:
break;
case NamespaceID:
{
NamespaceNode ns = (NamespaceNode)n;
if (ns.hasModifier(ASModifier.STATIC))
{
this.classScope.addProblem(new StaticNamespaceDefinitionProblem(ns));
}
else
{
try
{
this.classScope.traitsVisitor = itraits;
m_generator.generateInstructions(n, CmcEmitter.__statement_NT, this.classScope);
// assert(stmt_insns == null);
}
finally
{
this.classScope.traitsVisitor = null;
}
}
break;
}
default:
{
// support for class inits.
// loose statement are now collected in looseInsns
// Handle a loose statement.
InstructionList stmt_insns = m_generator.generateInstructions(n, CmcJSEmitter.__statement_NT, this.classScope);
if (stmt_insns != null)
{
if (looseInsns.size() == 0)
registerClassInit(n);
looseInsns.addAll(stmt_insns);
}
break;
}
}
}
private void registerClassInit(IASNode node)
{
final String fullName = JSGeneratingReducer.createFullNameFromDefinition(classScope.getProject(), classDefinition);
if (!fullName.equals(JSSharedData.JS_FRAMEWORK_NAME))
{
JSSharedData.instance.registerClassInit(fullName);
m_generator.getReducer().warnClassInitPerformance(node);
m_generator.getReducer().setNeedsSecondPass();
}
}
}