blob: a1ad565805055fce9d9f10eb1bfc95835caded76 [file] [log] [blame]
/*
* Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package com.apple.internal.jobjc.generator.utils;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import com.apple.internal.jobjc.generator.model.types.NType;
import com.apple.internal.jobjc.generator.model.types.NType.NArray;
import com.apple.internal.jobjc.generator.model.types.NType.NBitfield;
import com.apple.internal.jobjc.generator.model.types.NType.NClass;
import com.apple.internal.jobjc.generator.model.types.NType.NField;
import com.apple.internal.jobjc.generator.model.types.NType.NObject;
import com.apple.internal.jobjc.generator.model.types.NType.NPointer;
import com.apple.internal.jobjc.generator.model.types.NType.NPrimitive;
import com.apple.internal.jobjc.generator.model.types.NType.NSelector;
import com.apple.internal.jobjc.generator.model.types.NType.NStruct;
import com.apple.internal.jobjc.generator.model.types.NType.NUnion;
import com.apple.internal.jobjc.generator.model.types.NType.NUnknown;
import com.apple.internal.jobjc.generator.model.types.NType.NVoid;
import com.apple.internal.jobjc.generator.utils.Fp.Dispatcher;
import com.apple.internal.jobjc.generator.utils.Fp.Map2;
import com.apple.jobjc.JObjCRuntime.Width;
/**
* Merges two NTypes. All merge does is fill out missing information. It doesn't choose the larger primitive when there's a conflict or anything like that.
*
* Example:
*<pre>
* a: {_NSRect={_NSPoint="x"f"y"f}"size"{_NSSize=ff}}
* b: {_NSRect="origin"{_NSPoint=ff}{_NSSize="width"f"height"f}}
* c: {_NSRect="origin"{_NSPoint="x"f"y"f}"size"{_NSSize="width"f"height"f}}
*</pre>
*/
public class NTypeMerger {
public static class MergeFailed extends RuntimeException{
public MergeFailed(String reason, Object a, Object b){
super(reason
+ " -- (" + a.getClass().getSimpleName() + ") a: " + a
+ " -- (" + b.getClass().getSimpleName() + ") b: " + b);
}
}
private static NTypeMerger INST = new NTypeMerger();
public static NTypeMerger inst(){ return INST; }
/**
* Merge a and b.
*/
public NType merge(NType a, NType b) throws MergeFailed{
if(a!=null && b==null) return a;
if(a==null && b!=null) return b;
if(a==null && b==null) return null;
if(a.equals(b)) return a;
try {
return Dispatcher.dispatch(getClass(), this, "accept", a, b);
} catch (NoSuchMethodException e) {
throw new MergeFailed("a and b are of different NType", a, b);
}
}
private static Collection<String> emptyNames = Arrays.asList(null, "", "?");
/**
* Merge two identifiers:
* - If they're equal, return one.
* - If one is null, "", "?", return the other one.
* - else throw MergeFailed
*
* Exception: Due to a bug in BridgeSupport, this will return
* a (the first arg) instead of throwing MergeFailed.
*/
public String mergeName(String a, String b) throws MergeFailed{
if(QA.bothNullOrEquals(a, b)) return a;
if(emptyNames.contains(a) && !emptyNames.contains(b)) return b;
if(emptyNames.contains(b) && !emptyNames.contains(a)) return a;
return a; // HACK BS bug #5954843
// throw new MergeFailed("a and b have different names");
}
private Map mergeMap(Map a, Map b) throws MergeFailed{
if(a.equals(b)) return a;
Map ret = new HashMap();
Set keys = new HashSet(Fp.append(a.keySet(), b.keySet()));
for(Object key : keys){
Object ai = a.get(key);
Object bi = b.get(key);
if(ai != null && bi == null) ret.put(key, ai);
else if(ai == null && bi != null) ret.put(key, bi);
else if(ai.equals(bi)) ret.put(key, ai);
else throw new MergeFailed("a and b are different", ai, bi);
}
return ret;
}
public Map<Width,Integer> mergeSizeOf(Map<Width,Integer> a, Map<Width,Integer> b) throws MergeFailed{
return mergeMap(a, b);
}
public Map<Width,Integer> mergeOffset(Map<Width,Integer> a, Map<Width,Integer> b) throws MergeFailed{
return mergeMap(a, b);
}
//
private void mustEqual(NType a, NType b){
if(!a.equals(b)) throw new MergeFailed("a must equal b", a, b);
}
protected NType accept(NBitfield a, NBitfield b) {
mustEqual(a, b);
return a;
}
protected NType accept(NPrimitive a, NPrimitive b) {
mustEqual(a, b);
return a;
}
protected NType accept(NPointer a, NPointer b) {
return new NPointer(NTypeMerger.inst().merge(a.subject, b.subject));
}
// Merge structs
protected NField mergeNFields(NField a, NField b) {
return new NField(mergeName(a.name, b.name),
NTypeMerger.inst().merge(a.type, b.type),
mergeOffset(a.offset, b.offset));
}
protected NStruct mergeStructs(NStruct a, NStruct b){
if(a.fields.size() != b.fields.size())
throw new MergeFailed("a and b have different numbers of fields", a, b);
List<NField> fields = Fp.map2(new Map2<NField,NField,NField>(){
public NField apply(NField f32, NField f64) { return mergeNFields(f32, f64); }
}, a.fields, b.fields);
return new NStruct(mergeName(a.name, b.name),
fields, mergeSizeOf(a.sizeof, b.sizeof));
}
protected NType accept(NStruct a, NStruct b) {
return mergeStructs(a, b);
}
protected NType accept(NUnion a, NUnion b) {
NStruct nst = mergeStructs(a, b);
return new NUnion(nst.name, Fp.map(NUnion.zeroOffsets, nst.fields), nst.sizeof);
}
protected NType accept(NArray a, NArray b) {
if(a.length != b.length)
throw new MergeFailed("a and b are of different sizes", a, b);
return new NArray(a.length, NTypeMerger.inst().merge(a.type, b.type));
}
protected NType accept(NVoid a, NVoid b) { return NVoid.inst(); }
protected NType accept(NObject a, NObject b) { return NObject.inst(); }
protected NType accept(NClass a, NClass b) { return NClass.inst(); }
protected NType accept(NSelector a, NSelector b) { return NSelector.inst(); }
protected NType accept(NUnknown a, NUnknown b) { return NUnknown.inst(); }
}