github.com/tardisgo/tardisgo@v0.0.0-20161119180838-e0dd9a7e46b5/haxe/ops.go (about)

     1  // Copyright 2014 Elliott Stoneham and The TARDIS Go Authors
     2  // Use of this source code is governed by an MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package haxe
     6  
     7  import (
     8  	"fmt"
     9  	"go/types"
    10  
    11  	"golang.org/x/tools/go/ssa"
    12  )
    13  
    14  func (l langType) codeUnOp(regTyp types.Type, op string, v interface{}, CommaOK bool, errorInfo string) string {
    15  	useInt64 := false
    16  	lt := l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo)
    17  	if lt == "GOint64" {
    18  		useInt64 = true
    19  	}
    20  	rt := l.LangType(regTyp.Underlying(), false, errorInfo)
    21  	if lt != rt && op != "<-" && op != "*" {
    22  		l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeUnOp(): result type %s != source type %s", rt, lt))
    23  	}
    24  
    25  	// neko target platform requires special handling because in makes whole-number Float into Int without asking
    26  	// see: https://github.com/HaxeFoundation/haxe/issues/1282 which was marked as closed, but not fixed as at 2013.9.6
    27  
    28  	switch op {
    29  	case "<-":
    30  		l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeUnOp(): impossible to reach <- code"))
    31  		return ""
    32  	case "*":
    33  		goTyp := v.(ssa.Value).Type().Underlying().(*types.Pointer).Elem().Underlying()
    34  
    35  		//lt = l.LangType(goTyp, false, errorInfo)
    36  		iVal := "" + l.IndirectValue(v, errorInfo) + "" // need to cast it to pointer, when using -dce full and closures
    37  		//switch lt {
    38  		//case "Int":
    39  		//	return "(" + iVal + ".load()|0)" + fmt.Sprintf("/* %v %s */", goTyp, loadStoreSuffix(goTyp)) // force to Int for js, compiled platforms should optimize this away
    40  		//default:
    41  		//if strings.HasPrefix(lt, "Pointer") {
    42  		//	return "({var _v:PointerIF=" + iVal + `.load(); _v;})` // Ensure Haxe can work out that it is a pointer being returned
    43  		//}
    44  		if l.is1usePtr(v) {
    45  			oup, found := l.hc.map1usePtr[v.(ssa.Value)]
    46  			if !found {
    47  				panic(fmt.Sprintf("haxe.codeUnOp can't find oneUsePtr: %#v %s val %s=%s",
    48  					l.hc.map1usePtr, errorInfo, v.(ssa.Value).Name(), v.(ssa.Value).String()))
    49  			}
    50  			return oup.obj + ".get" + loadStoreSuffix(goTyp, true) + oup.off + ")"
    51  		}
    52  		if l.PogoComp().DebugFlag {
    53  			iVal = "Pointer.check(" + iVal + ")"
    54  		}
    55  		return iVal + ".load" + loadStoreSuffix(goTyp, false) + ")" + fmt.Sprintf("/* %v */ ", goTyp)
    56  		//}
    57  	case "-":
    58  		if l.LangType(v.(ssa.Value).Type().Underlying(), false, errorInfo) == "Complex" {
    59  			return "Complex.neg(" + l.IndirectValue(v, errorInfo) + ")"
    60  		}
    61  		fallthrough
    62  	default:
    63  		if useInt64 {
    64  			switch op { // roughly in the order of the GOint64 api spec
    65  			case "-":
    66  				return l.intTypeCoersion(v.(ssa.Value).Type().Underlying(),
    67  					"GOint64.neg("+l.IndirectValue(v, errorInfo)+")", errorInfo)
    68  			case "^":
    69  				return l.intTypeCoersion(v.(ssa.Value).Type().Underlying(),
    70  					"GOint64.xor("+l.IndirectValue(v, errorInfo)+",GOint64.make(-1,-1))", errorInfo)
    71  			default:
    72  				l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeUnOp(): unhandled Int64 un-op: %s", op))
    73  				return ""
    74  			}
    75  		} else {
    76  			valStr := l.IndirectValue(v, errorInfo)
    77  			switch v.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
    78  			case types.Uintptr: // although held as Dynamic, uintpointers are integers when doing calculations
    79  				valStr = "Force.toUint32(Force.toInt(" + valStr + "))"
    80  			case types.Float32:
    81  				valStr = "Force.toFloat32(" + valStr + ")"
    82  			case types.Float64:
    83  				valStr = "Force.toFloat(" + valStr + ")"
    84  			}
    85  			switch op {
    86  			case "^":
    87  				// Haxe has a different operator for bit-wise complement
    88  				return l.intTypeCoersion(regTyp.Underlying(),
    89  					"(~"+valStr+")", errorInfo)
    90  			case "-": //both negation and bit-complement can overflow
    91  				return l.intTypeCoersion(regTyp.Underlying(),
    92  					"(-"+valStr+")", errorInfo)
    93  			default: //no overflow issues, but let's be on the safe-side...
    94  				return l.intTypeCoersion(regTyp.Underlying(),
    95  					"("+op+valStr+")", errorInfo)
    96  			}
    97  		}
    98  	}
    99  }
   100  
   101  func (l langType) UnOp(register string, regTyp types.Type, op string, v interface{}, CommaOK bool, errorInfo string) string {
   102  	if op == "<-" { // wait for a channel to be ready
   103  		return l.Select(false, register, v, CommaOK, errorInfo)
   104  	}
   105  	return register + "=" + l.codeUnOp(regTyp, op, v, CommaOK, errorInfo) + ";"
   106  }
   107  
   108  func (l langType) codeBinOp(regTyp types.Type, op string, v1, v2 interface{}, errorInfo string) string {
   109  	ret := ""
   110  	useInt64 := false
   111  	v1LangType := l.LangType(v1.(ssa.Value).Type().Underlying(), false, errorInfo)
   112  	v2LangType := l.LangType(v2.(ssa.Value).Type().Underlying(), false, errorInfo)
   113  	if v1LangType != v2LangType && !(v1LangType == "Int" && v2LangType == "GOint64") && !(op == "<<" || op == ">>") {
   114  		l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): haxe types not equal: %s %s %s",
   115  			v1LangType, op, v2LangType))
   116  		return ""
   117  	}
   118  	rt := l.LangType(regTyp.Underlying(), false, errorInfo)
   119  	if v1LangType != rt && rt != "Bool" {
   120  		l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): result type %s != 1st operand type %s",
   121  			rt, v1LangType))
   122  	}
   123  
   124  	v1string := l.IndirectValue(v1, errorInfo)
   125  	v2string := l.IndirectValue(v2, errorInfo)
   126  	if v1LangType == "GOint64" {
   127  		useInt64 = true
   128  	}
   129  
   130  	// neko target platform requires special handling because in makes whole-number Float into Int without asking
   131  	// see: https://github.com/HaxeFoundation/haxe/issues/1282 which was marked as closed, but not fixed as at 2013.9.6
   132  	switch v1LangType {
   133  	case "Float":
   134  		v1string = "Force.toFloat(" + v1string + ")"
   135  	case "Dynamic": // assume it is a uintptr, so integer arithmetic is required
   136  		v1string = "Force.toUint32(Force.toInt(" + v1string + "))"
   137  	}
   138  	switch v2LangType {
   139  	case "Float":
   140  		v2string = "Force.toFloat(" + v2string + ")"
   141  	case "Dynamic": // assume it is a uintptr, so integer arithmetic is required
   142  		v2string = "Force.toUint32(Force.toInt(" + v2string + "))"
   143  	}
   144  
   145  	if v1LangType == "Complex" {
   146  		switch op {
   147  		case "+":
   148  			return "Complex.add(" + v1string + "," + v2string + ")"
   149  		case "/": // TODO review divide by zero error handling for this case (currently in Haxe Complex class)
   150  			return "Complex.div(" + v1string + "," + v2string + ")"
   151  		case "*":
   152  			return "Complex.mul(" + v1string + "," + v2string + ")"
   153  		case "-":
   154  			return "Complex.sub(" + v1string + "," + v2string + ")"
   155  		case "==":
   156  			return "Complex.eq(" + v1string + "," + v2string + ")"
   157  		case "!=":
   158  			return "Complex.neq(" + v1string + "," + v2string + ")"
   159  		default:
   160  			l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled Complex op: %s", op))
   161  			return ""
   162  		}
   163  
   164  	} else if v1LangType == "String" {
   165  		//switch op {
   166  		//case ">", "<", "<=", ">=":
   167  		//	return "(Go_haxegoruntime_StringCompare.callFromRT(this._goroutine," + v1string + "," + v2string +
   168  		//		")" + op + "0)"
   169  		//default:
   170  		return "(" + v1string + op + v2string + ")"
   171  		//}
   172  
   173  	} else if v1LangType == "Interface" {
   174  		switch op {
   175  		case "==":
   176  			return "Interface.isEqual(" + v1string + "," + v2string + ")"
   177  		case "!=":
   178  			return "!Interface.isEqual(" + v1string + "," + v2string + ")"
   179  		default:
   180  			l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled Interface op: %s", op))
   181  			return ""
   182  		}
   183  
   184  	} else if v1LangType == "Pointer" {
   185  		switch op {
   186  		case "==":
   187  			return "Pointer.isEqual(" + v1string + "," + v2string + ")"
   188  		case "!=":
   189  			return "!Pointer.isEqual(" + v1string + "," + v2string + ")"
   190  		default:
   191  			l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled Pointer op: %s", op))
   192  			return ""
   193  		}
   194  
   195  	} else if v1LangType == "Object" {
   196  		switch op {
   197  		case "==":
   198  			return "(" + v1string + ".isEqual(0," + v2string + ",0))"
   199  		case "!=":
   200  			return "!(" + v1string + ".isEqual(0," + v2string + ",0))"
   201  		default:
   202  			l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled Object op: %s", op))
   203  			return ""
   204  		}
   205  
   206  	} else {
   207  		if useInt64 { // explicitly enumerate all of the Int64 functions
   208  			isSignedStr := "true"
   209  			if (v1.(ssa.Value).Type().Underlying().(*types.Basic).Info() & types.IsUnsigned) != 0 {
   210  				isSignedStr = "false"
   211  			}
   212  
   213  			if op == "<<" || op == ">>" {
   214  				v2string = wrapForceToUInt(v2string, v2.(ssa.Value).Type().Underlying().(*types.Basic).Kind())
   215  			}
   216  
   217  			switch op { // roughly in the order of the GOint64 api spec
   218  			case "+":
   219  				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   220  					"GOint64.add("+v1string+","+v2string+")", errorInfo)
   221  			case "&":
   222  				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   223  					"GOint64.and("+v1string+","+v2string+")", errorInfo)
   224  			case "/":
   225  				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   226  					"GOint64.div("+v1string+","+v2string+","+isSignedStr+")", errorInfo)
   227  			case "%":
   228  				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   229  					"GOint64.mod("+v1string+","+v2string+","+isSignedStr+")", errorInfo)
   230  			case "*":
   231  				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   232  					"GOint64.mul("+v1string+","+v2string+")", errorInfo)
   233  			case "|":
   234  				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   235  					"GOint64.or("+v1string+","+v2string+")", errorInfo)
   236  			case "<<":
   237  				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   238  					"GOint64.shl("+v1string+","+v2string+")", errorInfo)
   239  			case ">>":
   240  				if isSignedStr == "true" {
   241  					ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   242  						"GOint64.shr("+v1string+","+v2string+")", errorInfo) // GOint64.shr does sign extension
   243  				} else {
   244  					ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   245  						"GOint64.ushr("+v1string+","+v2string+")", errorInfo) // GOint64.ushr does not do sign extension
   246  				}
   247  			case "-":
   248  				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   249  					"GOint64.sub("+v1string+","+v2string+")", errorInfo)
   250  			case "^":
   251  				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   252  					"GOint64.xor("+v1string+","+v2string+")", errorInfo)
   253  			case "&^":
   254  				v2string = "GOint64.xor(" + v2string + ",GOint64.make(-1,-1))"
   255  				ret = l.intTypeCoersion(v1.(ssa.Value).Type().Underlying(),
   256  					"GOint64.and("+v1string+","+v2string+")", errorInfo)
   257  			case "==", "!=", "<", ">", "<=", ">=":
   258  				compFunc := "GOint64.compare("
   259  				if (v1.(ssa.Value).Type().Underlying().(*types.Basic).Info() & types.IsUnsigned) != 0 {
   260  					compFunc = "GOint64.ucompare("
   261  				}
   262  				ret = "(" + compFunc + v1string + "," + v2string + ")" + op + "0)"
   263  			default:
   264  				l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled 64-bit op: %s", op))
   265  				return ""
   266  			}
   267  
   268  		} else {
   269  			switch op { // standard case, use Haxe operators
   270  			case "==", "!=", "<", ">", "<=", ">=": // no integer coersion, boolian results
   271  				switch v1.(ssa.Value).Type().Underlying().(type) {
   272  				case *types.Basic:
   273  					if (v1.(ssa.Value).Type().Underlying().(*types.Basic).Info() & types.IsUnsigned) != 0 {
   274  						if v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() == types.Uintptr {
   275  							// could be comparing pointers cast to uintptr, so force to uint
   276  							v1string = wrapForceToUInt(v1string, v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind())
   277  						}
   278  						if v2.(ssa.Value).Type().Underlying().(*types.Basic).Kind() == types.Uintptr {
   279  							// could be comparing pointers cast to uintptr, so force to uint
   280  							v2string = wrapForceToUInt(v2string, v2.(ssa.Value).Type().Underlying().(*types.Basic).Kind())
   281  						}
   282  						ret = "(Force.uintCompare(" + v1string + "," + v2string + ")" + op + "0)"
   283  					} else {
   284  						switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
   285  						case types.Float32:
   286  							// make sure we only compare the float32 bits
   287  							ret = "(" +
   288  								"Force.toFloat32(" +
   289  								v1string + ")" + op +
   290  								"Force.toFloat32(" +
   291  								v2string + ")" + ")"
   292  						default:
   293  							ret = "(" + v1string + op + v2string + ")"
   294  						}
   295  					}
   296  				default:
   297  					ret = "(" + v1string + op + v2string + ")"
   298  				}
   299  			case ">>", "<<":
   300  				//v1string = wrapForceToUInt(v1string, v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind())
   301  				v2string = wrapForceToUInt(v2string, v2.(ssa.Value).Type().Underlying().(*types.Basic).Kind())
   302  				switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
   303  				case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uintptr: // unsigned bit shift
   304  					if op == ">>" {
   305  						op = ">>>" // logical right shift if unsigned
   306  					}
   307  				}
   308  				bitlenMinus1 := fmt.Sprintf("%d", (haxeStdSizes.Sizeof(v1.(ssa.Value).Type().Underlying())*8)-1)
   309  				// TODO consider  putting this code in a Haxe function
   310  				ret = "({var _v1:Int=" + v1string + " ; var _v2:Int=" + v2string + " ; _v2==0?_v1" //NoOp if v2==0
   311  				// js requires this out-of-range test - TODO check other targets
   312  				ret += ":(_v2<0||_v2>" + bitlenMinus1 + "?" // outside chance very large shift appears -ve
   313  				switch op {
   314  				case ">>": // signed right shift >= bitlen
   315  					ret += "(_v1&(1<<" + bitlenMinus1 + ")!=0?-1:0)" // the sign must be extended if -ve
   316  				case ">>>": // unsigned right shift >= bitlen
   317  					ret += "0"
   318  				case "<<": // left shift >= bitlen
   319  					ret += "0"
   320  				default:
   321  					panic("haxe unhandled shift operation")
   322  				}
   323  				ret += ":_v1" + op + "_v2);})" // the actual op
   324  
   325  			case "/":
   326  				switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
   327  				case types.Int8:
   328  					ret = "Force.intDiv(" + v1string + "," + v2string + ",1)" // 1 byte special processing
   329  				case types.Int16:
   330  					ret = "Force.intDiv(" + v1string + "," + v2string + ",2)" // 2 byte special processing
   331  				case types.UntypedInt, types.Int, types.Int32: // treat all unknown int types as int 32
   332  					ret = "Force.intDiv(" + v1string + "," + v2string + ",4)" // 4 byte special processing
   333  				case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uintptr: // unsigned division
   334  					ret = "Force.intDiv(" + v1string + "," + v2string + ",0)" // spec does not require special processing, but is unsigned
   335  				case types.UntypedFloat, types.Float32, types.Float64:
   336  					ret = "Force.floatDiv(" + v1string + "," + v2string + ")"
   337  				default:
   338  					l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled divide type"))
   339  					ret = "(ERROR)"
   340  				}
   341  			case "%":
   342  				switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
   343  				case types.Int8:
   344  					ret = "Force.intMod(" + v1string + "," + v2string + ", 1)"
   345  				case types.Int16:
   346  					ret = "Force.intMod(" + v1string + "," + v2string + ", 2)"
   347  				case types.UntypedInt, types.Int, types.Int32: // treat all unknown int types as int 32
   348  					ret = "Force.intMod(" + v1string + "," + v2string + ", 4)"
   349  				case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uintptr: // unsigned mod
   350  					ret = "Force.intMod(" + v1string + "," + v2string + ", 0)"
   351  				case types.UntypedFloat, types.Float32, types.Float64:
   352  					ret = "Force.floatMod(" + v1string + "," + v2string + ")"
   353  				default:
   354  					l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled divide type"))
   355  					ret = "(ERROR)"
   356  				}
   357  
   358  			case "*":
   359  				switch v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind() {
   360  				case types.Int8:
   361  					ret = "Force.intMul(" + v1string + "," + v2string + ", 1)"
   362  				case types.Int16:
   363  					ret = "Force.intMul(" + v1string + "," + v2string + ", 2)"
   364  				case types.UntypedInt, types.Int, types.Int32: // treat all unknown int types as int 32
   365  					ret = "Force.intMul(" + v1string + "," + v2string + ", 4)"
   366  				case types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uintptr: // unsigned mod
   367  					ret = "Force.intMul(" + v1string + "," + v2string + ", 0)"
   368  				case types.UntypedFloat, types.Float32, types.Float64:
   369  					ret = "(" + v1string + "*" + v2string + ")"
   370  				default:
   371  					l.PogoComp().LogError(errorInfo, "Haxe", fmt.Errorf("codeBinOp(): unhandled divide type"))
   372  					ret = "(ERROR)"
   373  				}
   374  
   375  			case "&^":
   376  				// Haxe has a different operator for bit-wise complement ~, but using xor below
   377  				ret = "((" + v1string + ")&((" + v2string + ")^0xffffffff))"
   378  
   379  			case "&", "|", "^":
   380  				//v1string = wrapForceToUInt(v1string, v1.(ssa.Value).Type().Underlying().(*types.Basic).Kind())
   381  				//v2string = wrapForceToUInt(v2string, v2.(ssa.Value).Type().Underlying().(*types.Basic).Kind())
   382  				ret = "((" + v1string + ")" + op + "(" + v2string + "))"
   383  
   384  			case "+", "-":
   385  				ret = "(" + v1string + op + v2string + ")"
   386  
   387  			default:
   388  				panic("haxe unhandled binary operator: " + op)
   389  			}
   390  			ret = l.intTypeCoersion(
   391  				regTyp.Underlying(),
   392  				ret, errorInfo)
   393  
   394  		}
   395  		return ret
   396  	}
   397  }
   398  
   399  func (l langType) BinOp(register string, regTyp types.Type, op string, v1, v2 interface{}, errorInfo string) string {
   400  	return register + "=" + l.codeBinOp(regTyp, op, v1, v2, errorInfo) + ";"
   401  }