1 /*
2  * Copyright (c) 2011, 2012, Oracle and/or its affiliates. All rights reserved.
3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4  *
5  * This code is free software; you can redistribute it and/or modify it
6  * under the terms of the GNU General Public License version 2 only, as
7  * published by the Free Software Foundation.  Oracle designates this
8  * particular file as subject to the "Classpath" exception as provided
9  * by Oracle in the LICENSE file that accompanied this code.
10  *
11  * This code is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
14  * version 2 for more details (a copy is included in the LICENSE file that
15  * accompanied this code).
16  *
17  * You should have received a copy of the GNU General Public License version
18  * 2 along with this work; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20  *
21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22  * or visit www.oracle.com if you need additional information or have any
23  * questions.
24  */
25 package com.apple.internal.jobjc.generator.utils;
26 
27 import java.util.Arrays;
28 import java.util.Collection;
29 import java.util.HashMap;
30 import java.util.HashSet;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34 
35 import com.apple.internal.jobjc.generator.model.types.NType;
36 import com.apple.internal.jobjc.generator.model.types.NType.NArray;
37 import com.apple.internal.jobjc.generator.model.types.NType.NBitfield;
38 import com.apple.internal.jobjc.generator.model.types.NType.NClass;
39 import com.apple.internal.jobjc.generator.model.types.NType.NField;
40 import com.apple.internal.jobjc.generator.model.types.NType.NObject;
41 import com.apple.internal.jobjc.generator.model.types.NType.NPointer;
42 import com.apple.internal.jobjc.generator.model.types.NType.NPrimitive;
43 import com.apple.internal.jobjc.generator.model.types.NType.NSelector;
44 import com.apple.internal.jobjc.generator.model.types.NType.NStruct;
45 import com.apple.internal.jobjc.generator.model.types.NType.NUnion;
46 import com.apple.internal.jobjc.generator.model.types.NType.NUnknown;
47 import com.apple.internal.jobjc.generator.model.types.NType.NVoid;
48 import com.apple.internal.jobjc.generator.utils.Fp.Dispatcher;
49 import com.apple.internal.jobjc.generator.utils.Fp.Map2;
50 import com.apple.jobjc.JObjCRuntime.Width;
51 
52 /**
53  * 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.
54  *
55  * Example:
56  *<pre>
57  * a: {_NSRect={_NSPoint="x"f"y"f}"size"{_NSSize=ff}}
58  * b: {_NSRect="origin"{_NSPoint=ff}{_NSSize="width"f"height"f}}
59  * c: {_NSRect="origin"{_NSPoint="x"f"y"f}"size"{_NSSize="width"f"height"f}}
60  *</pre>
61  */
62 public class NTypeMerger {
63     public static class MergeFailed extends RuntimeException{
MergeFailed(String reason, Object a, Object b)64         public MergeFailed(String reason, Object a, Object b){
65             super(reason
66                     + " -- (" + a.getClass().getSimpleName() + ") a: " + a
67                     + " -- (" + b.getClass().getSimpleName() + ") b: " + b);
68         }
69     }
70 
71     private static NTypeMerger INST = new NTypeMerger();
inst()72     public static NTypeMerger inst(){ return INST; }
73 
74     /**
75      * Merge a and b.
76      */
merge(NType a, NType b)77     public NType merge(NType a, NType b) throws MergeFailed{
78         if(a!=null && b==null) return a;
79         if(a==null && b!=null) return b;
80         if(a==null && b==null) return null;
81         if(a.equals(b)) return a;
82         try {
83             return Dispatcher.dispatch(getClass(), this, "accept", a, b);
84         } catch (NoSuchMethodException e) {
85             throw new MergeFailed("a and b are of different NType", a, b);
86         }
87     }
88 
89     private static Collection<String> emptyNames = Arrays.asList(null, "", "?");
90     /**
91      * Merge two identifiers:
92      *  - If they're equal, return one.
93      *  - If one is null, "", "?", return the other one.
94      *  - else throw MergeFailed
95      *
96      *  Exception: Due to a bug in BridgeSupport, this will return
97      *  a (the first arg) instead of throwing MergeFailed.
98      */
mergeName(String a, String b)99     public String mergeName(String a, String b) throws MergeFailed{
100         if(QA.bothNullOrEquals(a, b)) return a;
101         if(emptyNames.contains(a) && !emptyNames.contains(b)) return b;
102         if(emptyNames.contains(b) && !emptyNames.contains(a)) return a;
103         return a; // HACK BS bug #5954843
104 //        throw new MergeFailed("a and b have different names");
105     }
106 
mergeMap(Map a, Map b)107     private Map mergeMap(Map a, Map b) throws MergeFailed{
108         if(a.equals(b)) return a;
109         Map ret = new HashMap();
110         Set keys = new HashSet(Fp.append(a.keySet(), b.keySet()));
111         for(Object key : keys){
112             Object ai = a.get(key);
113             Object bi = b.get(key);
114             if(ai != null && bi == null) ret.put(key, ai);
115             else if(ai == null && bi != null) ret.put(key, bi);
116             else if(ai.equals(bi)) ret.put(key, ai);
117             else throw new MergeFailed("a and b are different", ai, bi);
118         }
119         return ret;
120     }
121 
mergeSizeOf(Map<Width,Integer> a, Map<Width,Integer> b)122     public Map<Width,Integer> mergeSizeOf(Map<Width,Integer> a, Map<Width,Integer> b) throws MergeFailed{
123         return mergeMap(a, b);
124     }
125 
mergeOffset(Map<Width,Integer> a, Map<Width,Integer> b)126     public Map<Width,Integer> mergeOffset(Map<Width,Integer> a, Map<Width,Integer> b) throws MergeFailed{
127         return mergeMap(a, b);
128     }
129 
130     //
131 
mustEqual(NType a, NType b)132     private void mustEqual(NType a, NType b){
133         if(!a.equals(b)) throw new MergeFailed("a must equal b", a, b);
134     }
135 
accept(NBitfield a, NBitfield b)136     protected NType accept(NBitfield a, NBitfield b) {
137         mustEqual(a, b);
138         return a;
139     }
140 
accept(NPrimitive a, NPrimitive b)141     protected NType accept(NPrimitive a, NPrimitive b) {
142         mustEqual(a, b);
143         return a;
144     }
145 
accept(NPointer a, NPointer b)146     protected NType accept(NPointer a, NPointer b) {
147         return new NPointer(NTypeMerger.inst().merge(a.subject, b.subject));
148     }
149 
150     // Merge structs
151 
mergeNFields(NField a, NField b)152     protected NField mergeNFields(NField a, NField b) {
153         return new NField(mergeName(a.name, b.name),
154                 NTypeMerger.inst().merge(a.type, b.type),
155                 mergeOffset(a.offset, b.offset));
156     }
157 
mergeStructs(NStruct a, NStruct b)158     protected NStruct mergeStructs(NStruct a, NStruct b){
159         if(a.fields.size() != b.fields.size())
160             throw new MergeFailed("a and b have different numbers of fields", a, b);
161 
162         List<NField> fields = Fp.map2(new Map2<NField,NField,NField>(){
163             public NField apply(NField f32, NField f64) { return mergeNFields(f32, f64); }
164         }, a.fields, b.fields);
165 
166         return new NStruct(mergeName(a.name, b.name),
167                 fields, mergeSizeOf(a.sizeof, b.sizeof));
168     }
169 
accept(NStruct a, NStruct b)170     protected NType accept(NStruct a, NStruct b) {
171         return mergeStructs(a, b);
172     }
173 
accept(NUnion a, NUnion b)174     protected NType accept(NUnion a, NUnion b) {
175         NStruct nst = mergeStructs(a, b);
176         return new NUnion(nst.name, Fp.map(NUnion.zeroOffsets, nst.fields), nst.sizeof);
177     }
178 
accept(NArray a, NArray b)179     protected NType accept(NArray a, NArray b) {
180         if(a.length != b.length)
181             throw new MergeFailed("a and b are of different sizes", a, b);
182         return new NArray(a.length, NTypeMerger.inst().merge(a.type, b.type));
183     }
184 
accept(NVoid a, NVoid b)185     protected NType accept(NVoid a, NVoid b) { return NVoid.inst(); }
accept(NObject a, NObject b)186     protected NType accept(NObject a, NObject b) { return NObject.inst(); }
accept(NClass a, NClass b)187     protected NType accept(NClass a, NClass b) { return NClass.inst(); }
accept(NSelector a, NSelector b)188     protected NType accept(NSelector a, NSelector b) { return NSelector.inst(); }
accept(NUnknown a, NUnknown b)189     protected NType accept(NUnknown a, NUnknown b) { return NUnknown.inst(); }
190 }
191