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