blob: 0298127ac2112b0947bf9de4c21a5ba003710dcf [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.royale.compiler.internal.abc;
import java.util.Vector;
import org.apache.royale.abc.ABCConstants;
import org.apache.royale.abc.semantics.ClassInfo;
import org.apache.royale.abc.semantics.MethodInfo;
import org.apache.royale.abc.semantics.Name;
import org.apache.royale.abc.semantics.Namespace;
import org.apache.royale.abc.semantics.Nsset;
import org.apache.royale.abc.semantics.PooledValue;
import org.apache.royale.abc.semantics.Traits;
import org.apache.royale.abc.visitors.ITraitVisitor;
import org.apache.royale.abc.visitors.ITraitsVisitor;
import org.apache.royale.compiler.constants.IASLanguageConstants;
import org.apache.royale.compiler.definitions.IParameterDefinition;
import org.apache.royale.compiler.definitions.references.INamespaceReference;
import org.apache.royale.compiler.definitions.references.IReference;
import org.apache.royale.compiler.definitions.references.ReferenceFactory;
import org.apache.royale.compiler.internal.definitions.ClassDefinition;
import org.apache.royale.compiler.internal.definitions.ConstantDefinition;
import org.apache.royale.compiler.internal.definitions.DefinitionBase;
import org.apache.royale.compiler.internal.definitions.FunctionDefinition;
import org.apache.royale.compiler.internal.definitions.GetterDefinition;
import org.apache.royale.compiler.internal.definitions.NamespaceDefinition;
import org.apache.royale.compiler.internal.definitions.ParameterDefinition;
import org.apache.royale.compiler.internal.definitions.SetterDefinition;
import org.apache.royale.compiler.internal.definitions.TypeDefinitionBase;
import org.apache.royale.compiler.internal.definitions.VariableDefinition;
import org.apache.royale.compiler.internal.scopes.ASScope;
import org.apache.royale.compiler.internal.scopes.FunctionScope;
import org.apache.royale.compiler.scopes.IASScope;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
/**
* This {@link ITraitsVisitor} creates definition for each trait, and add the
* definitions to a scope object.
*/
public class ScopedDefinitionTraitsVisitor implements ITraitsVisitor
{
private static final IReference TYPE_ANY = ReferenceFactory.builtinReference(IASLanguageConstants.BuiltinType.ANY_TYPE);
private static final IReference TYPE_FUNCTION = ReferenceFactory.builtinReference(IASLanguageConstants.BuiltinType.FUNCTION);
private final ASScope scope;
private final boolean isStatic;
private final ABCScopeBuilder scopeBuilder;
private final INamespaceReference interfNamespace;
public ScopedDefinitionTraitsVisitor(final ABCScopeBuilder owner, final IASScope scope,
boolean isStatic)
{
this(owner, scope, isStatic, null);
}
public ScopedDefinitionTraitsVisitor(final ABCScopeBuilder owner, final IASScope scope,
boolean isStatic, INamespaceReference interfNamespace)
{
assert scope instanceof ASScope;
this.scopeBuilder = owner;
this.scope = (ASScope)scope;
this.isStatic = isStatic;
this.interfNamespace = interfNamespace;
}
@Override
public ITraitVisitor visitSlotTrait(int kind, Name name, int slot_id, Name slot_type, Object slot_value)
{
final String definitionName = getDefinitionName(name);
final DefinitionBase def;
kind &= ABCConstants.TRAIT_KIND_MASK;
switch (kind)
{
case ABCConstants.KIND_SLOT:
def = new VariableDefinition(definitionName, slot_value);
break;
case ABCConstants.KIND_CONST:
if (slot_value instanceof Namespace)
def = NamespaceDefinition.createNamespaceDefinition(definitionName, (Namespace)slot_value);
else
def = new ConstantDefinition(definitionName, slot_value);
break;
default:
throw new IllegalStateException("Invalid slot kind: " + kind);
}
final INamespaceReference namespaceReference = getNamespaceReference(name);
def.setNamespaceReference(namespaceReference);
if (isStatic)
def.setStatic();
def.setTypeReference(slot_type == null ? TYPE_ANY : scopeBuilder.getReference(slot_type));
scope.addDefinition(def);
return new CollectMetadataTraitVisitor(def);
}
@Override
public ITraitVisitor visitMethodTrait(int kind, Name name, int disp_id, MethodInfo method)
{
final String definitionName = getDefinitionName(name);
FunctionDefinition methodDef;
kind &= ABCConstants.TRAIT_KIND_MASK;
switch (kind)
{
case ABCConstants.KIND_METHOD:
methodDef = new FunctionDefinition(definitionName);
break;
case ABCConstants.KIND_GETTER:
methodDef = new GetterDefinition(definitionName);
break;
case ABCConstants.KIND_SETTER:
methodDef = new SetterDefinition(definitionName);
break;
case ABCConstants.KIND_FUNCTION:
methodDef = new FunctionDefinition(definitionName);
break;
default:
throw new IllegalStateException("Invalid method kind:" + kind);
}
final INamespaceReference namespaceReference = getNamespaceReference(name);
methodDef.setNamespaceReference(namespaceReference);
int paramTypesSize = method.getParamTypes().size();
final ParameterDefinition params[] = new ParameterDefinition[paramTypesSize + (method.needsRest() ? 1 : 0)];
if (params.length > 0)
{
ASScope methodScope = new FunctionScope(scope);
methodScope.setContainingDefinition(methodDef);
methodDef.setContainedScope(methodScope);
Vector<PooledValue> defaultValues = method.getDefaultValues();
int firstOptionalParam = paramTypesSize - defaultValues.size();
for (int i = 0; i < paramTypesSize; i++)
{
final Name paramType = method.getParamTypes().get(i);
final String paramName = i < method.getParamNames().size() ? method.getParamNames().get(i) : MethodInfo.UNKNOWN_PARAM_NAME;
params[i] = new ParameterDefinition(paramName);
params[i].setTypeReference(paramType == null ? TYPE_ANY : scopeBuilder.getReference(paramType));
if (i >= firstOptionalParam)
{
Object defaultValue = defaultValues.get(i - firstOptionalParam).getValue();
params[i].setDefaultValue(defaultValue);
}
methodScope.addDefinition(params[i]);
}
if( method.needsRest() )
{
ParameterDefinition rest = new ParameterDefinition(MethodInfo.UNKNOWN_PARAM_NAME);
rest.setRest();
rest.setTypeReference(ReferenceFactory.builtinReference(IASLanguageConstants.BuiltinType.ARRAY));
params[paramTypesSize] = rest;
}
}
methodDef.setParameters(params);
Name returnType = method.getReturnType();
methodDef.setReturnTypeReference(returnType == null ? TYPE_ANY : scopeBuilder.getReference(returnType));
// The type of a getter or setter is its property type
// (i.e., the getter's return type or the setter's parameter type).
// The type of a method or function is "Function".
switch (kind)
{
case ABCConstants.KIND_GETTER:
methodDef.setTypeReference(methodDef.getReturnTypeReference());
break;
case ABCConstants.KIND_SETTER:
methodDef.setTypeReference(methodDef.getParameters()[0].getTypeReference());
break;
case ABCConstants.KIND_METHOD:
case ABCConstants.KIND_FUNCTION:
methodDef.setTypeReference(TYPE_FUNCTION);
break;
default:
throw new IllegalStateException("Invalid method kind:" + kind);
}
if (isStatic)
methodDef.setStatic();
scope.addDefinition(methodDef);
return new CollectMetadataTraitVisitor(methodDef);
}
@Override
public ITraitVisitor visitClassTrait(int kind, Name name, int slot_id, ClassInfo clazz)
{
final TypeDefinitionBase classDef = scopeBuilder.classDefinitions.get(clazz);
assert classDef != null : "Null class def at #" + slot_id;
scope.addDefinition(classDef);
classDef.getContainedScope().setContainingScope(scope);
// Need to setup the scopes for the constructor and any params
// here instead of ABCScopeBuilder, as we need to have a handle to the
// class scope which isn't set until here.
if (classDef instanceof ClassDefinition)
{
FunctionDefinition ctor = (FunctionDefinition)((ClassDefinition)classDef).getConstructor();
classDef.getContainedScope().addDefinition(ctor);
IParameterDefinition[] params = ctor.getParameters();
if (params.length > 0)
{
ASScope ctorScope = new FunctionScope(scope);
ctorScope.setContainingDefinition(ctor);
ctor.setContainedScope(ctorScope);
for (IParameterDefinition param : params)
{
ctorScope.addDefinition(param);
}
}
}
return new CollectMetadataTraitVisitor(classDef);
}
private static boolean legalDefinitionNsset(Nsset nsSet)
{
if (nsSet == null)
return false;
if (nsSet.length() == 1)
return true;
return Iterables.all(nsSet, new Predicate<Namespace>() {
@Override
public boolean apply(Namespace ns)
{
return ns.getApiVersion() != ABCConstants.NO_API_VERSION;
}
@Override
public boolean test(Namespace input)
{
return apply(input);
}
});
}
public static String getDefinitionName(Name name)
{
final String baseName = name.getBaseName();
// A definition can only ever have one namespace entry
// otherwise it is an invalid SWC.
Nsset nsSet = name.getQualifiers();
if (!legalDefinitionNsset(nsSet))
{
throw new IllegalStateException("Definition " + baseName + " can have only one qualifier or all qualifiers should be versioned namespaces");
}
return baseName;
}
private INamespaceReference getNamespaceReference(Name name)
{
final Namespace namespace = Iterables.getFirst(name.getQualifiers(), null);
assert namespace != null;
INamespaceReference namespaceReference =
(INamespaceReference)scopeBuilder.getNamespaceReferenceForNamespace(namespace);
// Interface Namespaces are encoded as regular user defined namespaces in the ABC, but internally
// we want them to be InterfaceNamespaceDefinitions. If we come across a user defined namespace while
// building the traits for an interface, and it matches the interface namespace, then use the interface
// namespace instead so that other processing that relies on InterfaceNamespaceDefinitions works right.
if( interfNamespace != null && interfNamespace.equals(namespaceReference) )
namespaceReference = interfNamespace;
return namespaceReference;
}
@Override
public void visit()
{
}
@Override
public void visitEnd()
{
}
@Override
public Traits getTraits()
{
return null;
}
}