blob: ed193651165357df1aae72c3792547b4ec482331 [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 static com.google.common.base.Preconditions.checkNotNull;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import org.apache.royale.abc.ABCConstants;
import org.apache.royale.abc.ABCParser;
import org.apache.royale.abc.semantics.ClassInfo;
import org.apache.royale.abc.semantics.InstanceInfo;
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.visitors.IClassVisitor;
import org.apache.royale.abc.visitors.IMethodVisitor;
import org.apache.royale.abc.visitors.IScriptVisitor;
import org.apache.royale.abc.visitors.NilABCVisitor;
import org.apache.royale.compiler.constants.IASLanguageConstants;
import org.apache.royale.compiler.definitions.INamespaceDefinition;
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.FunctionDefinition;
import org.apache.royale.compiler.internal.definitions.InterfaceDefinition;
import org.apache.royale.compiler.internal.definitions.NamespaceDefinition;
import org.apache.royale.compiler.internal.definitions.ParameterDefinition;
import org.apache.royale.compiler.internal.definitions.TypeDefinitionBase;
import org.apache.royale.compiler.internal.scopes.ASFileScope;
import org.apache.royale.compiler.internal.workspaces.Workspace;
import org.apache.royale.compiler.scopes.IASScope;
import org.apache.royale.compiler.scopes.IFileScopeProvider;
import org.apache.royale.compiler.workspaces.IWorkspace;
/**
* Populates symbol table from an ABC file.
*/
public class ABCScopeBuilder extends NilABCVisitor
{
private static final IReference TYPE_ANY = ReferenceFactory.builtinReference(IASLanguageConstants.BuiltinType.ANY_TYPE);
private static final IReference TYPE_FUNCTION = ReferenceFactory.builtinReference(IASLanguageConstants.BuiltinType.FUNCTION);
IReference getReference(Name name)
{
if( name == null )
return null;
IReference ref = nameMap.get(name);
if( ref != null )
return ref;
switch( name.getKind() )
{
case ABCConstants.CONSTANT_Qname:
INamespaceDefinition ns = getNamespaceReferenceForNamespace(name.getSingleQualifier());
ref = ReferenceFactory.resolvedQualifierQualifiedReference(workspace, ns, name.getBaseName());
break;
case ABCConstants.CONSTANT_Multiname:
Nsset set = name.getQualifiers();
if (set.length() != 1)
{
Set<INamespaceDefinition> ns_set = new HashSet<INamespaceDefinition>(set.length());
for( Namespace n : set )
ns_set.add(getNamespaceReferenceForNamespace(n));
ref = ReferenceFactory.multinameReference(workspace, ns_set, name.getBaseName());
}
else
{
INamespaceDefinition singleNS = getNamespaceReferenceForNamespace(name.getSingleQualifier());
ref = ReferenceFactory.resolvedQualifierQualifiedReference(workspace, singleNS, name.getBaseName());
}
break;
case ABCConstants.CONSTANT_TypeName:
// If we ever support more than Vector, we'll need to harden this code against loops
// in the type name's.
assert name.getTypeNameBase().getBaseName().equals("Vector") : "Vector is currently the only supported parameterized type!";
IReference parameterizedTypeReference = getReference(name.getTypeNameBase());
IReference parameterTypeReference = getReference(name.getTypeNameParameter());
ref = ReferenceFactory.parameterizedReference(workspace, parameterizedTypeReference, parameterTypeReference);
break;
default:
assert false : "Unsupported multiname type: " + name.getKind();
}
nameMap.put(name, ref);
return ref;
}
/**
* Encode a {@link Name} that refers to a global AS3 definition as a string.
* If the number of qualifiers in the {@link Name} is greater than one this
* method will use the first qualifier that is either of type
* {@link ABCConstants#CONSTANT_PackageNs} or
* {@link ABCConstants#CONSTANT_PackageInternalNs}.
*
* @param name {@link Name} to encode in a String.
* @return A String that attempts to encode the specified {@link Name}.
*/
static String getQName(Name name)
{
if (name == null)
return null;
String baseName = name.getBaseName();
// Look through the multiname set for a package name.
// TODO Although most names in SWCs seem to have only
// one namespace in their namespace set, interfaces seem
// to have true multinames with multiple namespaces.
// For now, just look for the package namespace.
// Eventually we have to deal with a real multiname.
String packageName = null;
Nsset qualifiers = name.getQualifiers();
if (qualifiers != null)
{
for (Namespace namespace : qualifiers)
{
if ((namespace.getKind() == ABCConstants.CONSTANT_PackageNs) || (namespace.getKind() == ABCConstants.CONSTANT_PackageInternalNs))
{
packageName = namespace.getName();
if (packageName.length() > 0)
break;
}
}
}
return packageName != null && packageName.length() > 0 ?
packageName + '.' + baseName :
baseName;
}
private IReference[] getReferences(Name[] names)
{
IReference[] refs = null;
int n = names.length;
if (n != 0)
{
refs = new IReference[n];
for (int i = 0; i < n; i++)
{
refs[i] = getReference(names[i]);
}
}
return refs;
}
/**
* Create an ABCScopeBuilder from ABC byte code data.
*
* @param workspace workspace
* @param abcData ABC byte code data.
* @param path path of the file that contains the abc data.
* @param fileScopeProvider callback that creates {@code ASFileScope}
* objects.
*/
public ABCScopeBuilder(final IWorkspace workspace,
final byte[] abcData,
final String path,
final IFileScopeProvider fileScopeProvider)
{
checkNotNull(workspace, "Workspace can't be null.");
checkNotNull(abcData, "ABC data can't be null.");
checkNotNull(path, "File path can't be null.");
checkNotNull(fileScopeProvider, "File scope provider can't be null.");
scopes = new ArrayList<IASScope>();
classDefinitions = new HashMap<ClassInfo, TypeDefinitionBase>();
abcParser = new ABCParser(abcData);
namespacesMap = new HashMap<Namespace, INamespaceDefinition>();
nameMap = new HashMap<Name, IReference>();
this.workspace = workspace;
this.path = path;
this.fileScopeProvider = fileScopeProvider;
}
private final IFileScopeProvider fileScopeProvider;
private final ABCParser abcParser;
private final List<IASScope> scopes;
// This is the class definition pool.
protected final Map<ClassInfo, TypeDefinitionBase> classDefinitions;
private final Map<Namespace, INamespaceDefinition> namespacesMap;
private final Map<Name, IReference> nameMap;
private final IWorkspace workspace;
/**
* Path of the file that contains the abc data. This field is used to set
* the containing file path of the definitions built from ABC.
*/
protected final String path;
/**
* Constructs or otherwise obtains an {@link INamespaceReference} for an
* {@link Namespace}.
*
* @param ns {@link Namespace} for which an {@link INamespaceReference}
* should be obtained.
* @return A {@link INamespaceReference} that wraps the specified
* {@link Namespace}.
*/
public INamespaceDefinition getNamespaceReferenceForNamespace(Namespace ns)
{
INamespaceDefinition result = namespacesMap.get(ns);
if (result != null)
return result;
// Strip off versioning information.
Namespace nonVersionedNS = ns.getApiVersion() == ABCConstants.NO_API_VERSION?
ns :
new Namespace(ns.getKind(), ns.getName());
;
result = NamespaceDefinition.createNamespaceDefinition(nonVersionedNS);
assert result != null;
namespacesMap.put(ns, result);
return result;
}
/**
* Build scopes and symbol tables from ABC.
*
* @return the script definition object
* @throws IOException error
*/
public List<IASScope> build() throws IOException
{
abcParser.parseABC(this);
return this.scopes;
}
@Override
public IScriptVisitor visitScript()
{
final ASFileScope fileScope = this.fileScopeProvider.createFileScope(workspace, path);
assert fileScope != null : "IFileScopeProvider shouldn't create null objects.";
scopes.add(fileScope);
return new ScriptDefinitionBuilder(this, fileScope);
}
/**
* Visit class definition pool. Build a local map from classInfo to
* ClassDefinition. The pool is queried by children visitors.
* <p>
* <b>InstanceInfo.Flags</b>
* <ul>
* <li>ClassSealed=0x01</li>
* <li>ClassFinal=0x02</li>
* <li>ClassInterface=0x04</li>
* <li>ClassProtectedNs=0x08</li>
* </ul>
*/
@Override
public IClassVisitor visitClass(InstanceInfo iinfo, ClassInfo cinfo)
{
// Instance flags
final boolean isSealed = (iinfo.flags & ABCConstants.CONSTANT_ClassSealed) != 0;
final boolean isFinal = (iinfo.flags & ABCConstants.CONSTANT_ClassFinal) != 0;
final boolean isInterface = (iinfo.flags & ABCConstants.CONSTANT_ClassInterface) != 0;
assert iinfo.name.getKind() == ABCConstants.CONSTANT_Qname;
String typeName = iinfo.name.getBaseName();
final Namespace namespace = iinfo.name.getSingleQualifier();
final String namespaceName = namespace.getName();
final int namespaceKind = namespace.getKind();
INamespaceReference namespaceRef = null;
if (namespaceName.length() != 0 &&
((namespaceKind == ABCConstants.CONSTANT_PackageNs) || (namespaceKind == ABCConstants.CONSTANT_PackageInternalNs)))
{
namespaceRef =
((Workspace)workspace).getPackageNamespaceDefinitionCache().get(namespaceName, namespaceKind == ABCConstants.CONSTANT_PackageInternalNs);
}
else
{
namespaceRef = NamespaceDefinition.createNamespaceDefinition(namespace);
}
final TypeDefinitionBase typeDefinition;
if (isInterface)
{
final InterfaceDefinition interfaceDefinition = new InterfaceDefinition(typeName);
final IReference[] extendedInterfaces = getReferences(iinfo.interfaceNames);
interfaceDefinition.setExtendedInterfaceReferences(extendedInterfaces);
setupCastFunction(iinfo, interfaceDefinition);
typeDefinition = interfaceDefinition;
}
else
{
String protectedNSURI;
if (iinfo.hasProtectedNs())
protectedNSURI = iinfo.protectedNs.getName();
else
{
String classNSURI = namespace.getName();
protectedNSURI = (classNSURI.isEmpty() ? "" : classNSURI + ":") + typeName;
}
NamespaceDefinition.IProtectedNamespaceDefinition protectedNSDefinition = NamespaceDefinition.createProtectedNamespaceDefinition(protectedNSURI);
final ClassDefinition classDefinition = new ClassDefinition(typeName, namespaceRef, protectedNSDefinition);
final IReference baseClass = getReference(iinfo.superName);
classDefinition.setBaseClassReference(baseClass);
final IReference[] implementedInterfaces = getReferences(iinfo.interfaceNames);
classDefinition.setImplementedInterfaceReferences(implementedInterfaces);
setupConstructor(iinfo, classDefinition);
typeDefinition = classDefinition;
}
final INamespaceDefinition namespaceReference = getNamespaceReferenceForNamespace(namespace);
typeDefinition.setNamespaceReference((INamespaceReference)namespaceReference);
if (!isSealed)
typeDefinition.setDynamic();
if (isFinal)
typeDefinition.setFinal();
final TypeDefinitionBuilder visitor = new TypeDefinitionBuilder(this, typeDefinition);
classDefinitions.put(cinfo, typeDefinition);
return visitor;
}
@Override
public IMethodVisitor visitMethod(MethodInfo minfo)
{
return null;
}
private void setupConstructor(InstanceInfo iinfo, ClassDefinition classDefinition)
{
String ctorName = ScopedDefinitionTraitsVisitor.getDefinitionName(iinfo.name);
FunctionDefinition ctor = new FunctionDefinition(ctorName);
ctor.setNamespaceReference(NamespaceDefinition.getCodeModelImplicitDefinitionNamespace());
ctor.setTypeReference(TYPE_FUNCTION);
// NOTE: don't set a return type for constructors
ctor.setReturnTypeReference(null);
MethodInfo mInfo = iinfo.iInit;
int paramTypesSize = mInfo.getParamTypes().size();
final ParameterDefinition params[] = new ParameterDefinition[paramTypesSize + (mInfo.needsRest() ? 1 : 0)];
if (params.length > 0)
{
Vector<PooledValue> defaultValues = mInfo.getDefaultValues();
int firstOptionalParam = paramTypesSize - defaultValues.size();
for (int i = 0; i < paramTypesSize; i++)
{
final Name paramType = mInfo.getParamTypes().get(i);
final String paramName = i < mInfo.getParamNames().size() ? mInfo.getParamNames().get(i) : MethodInfo.UNKNOWN_PARAM_NAME;
params[i] = new ParameterDefinition(paramName);
params[i].setTypeReference(paramType == null ? TYPE_ANY : getReference(paramType));
if (i >= firstOptionalParam)
{
Object defaultValue = defaultValues.get(i - firstOptionalParam).getValue();
params[i].setDefaultValue(defaultValue);
}
}
if (mInfo.needsRest())
{
ParameterDefinition rest = new ParameterDefinition(MethodInfo.UNKNOWN_PARAM_NAME);
rest.setRest();
rest.setTypeReference(ReferenceFactory.builtinReference(IASLanguageConstants.BuiltinType.ARRAY));
params[paramTypesSize] = rest;
}
}
ctor.setParameters(params);
ctor.setAsConstructor(classDefinition);
ctor.setImplicit();
}
private void setupCastFunction(InstanceInfo iinfo, InterfaceDefinition interfaceDefinition)
{
String castName = ScopedDefinitionTraitsVisitor.getDefinitionName(iinfo.name);
FunctionDefinition castFunc = new FunctionDefinition(castName);
castFunc.setNamespaceReference(NamespaceDefinition.getCodeModelImplicitDefinitionNamespace());
castFunc.setReturnTypeReference(ReferenceFactory.resolvedReference(interfaceDefinition));
castFunc.setCastFunction();
castFunc.setImplicit();
}
}