Java tutorial
/* * Copyright (c) 2018, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. * * WSO2 Inc. 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.ballerinalang.bre.bvm; import org.apache.commons.lang3.StringEscapeUtils; import org.ballerinalang.bre.BLangCallableUnitCallback; import org.ballerinalang.bre.Context; import org.ballerinalang.bre.NativeCallContext; import org.ballerinalang.bre.old.WorkerExecutionContext; import org.ballerinalang.channels.ChannelManager; import org.ballerinalang.channels.ChannelRegistry; import org.ballerinalang.model.NativeCallableUnit; import org.ballerinalang.model.types.BArrayType; import org.ballerinalang.model.types.BAttachedFunction; import org.ballerinalang.model.types.BErrorType; import org.ballerinalang.model.types.BField; import org.ballerinalang.model.types.BFiniteType; import org.ballerinalang.model.types.BFunctionType; import org.ballerinalang.model.types.BFutureType; import org.ballerinalang.model.types.BJSONType; import org.ballerinalang.model.types.BMapType; import org.ballerinalang.model.types.BObjectType; import org.ballerinalang.model.types.BRecordType; import org.ballerinalang.model.types.BStreamType; import org.ballerinalang.model.types.BStructureType; import org.ballerinalang.model.types.BTableType; import org.ballerinalang.model.types.BTupleType; import org.ballerinalang.model.types.BType; import org.ballerinalang.model.types.BTypes; import org.ballerinalang.model.types.BUnionType; import org.ballerinalang.model.types.TypeConstants; import org.ballerinalang.model.types.TypeTags; import org.ballerinalang.model.util.DecimalValueKind; import org.ballerinalang.model.util.Flags; import org.ballerinalang.model.util.JSONUtils; import org.ballerinalang.model.util.ListUtils; import org.ballerinalang.model.util.StringUtils; import org.ballerinalang.model.util.XMLUtils; import org.ballerinalang.model.values.BBoolean; import org.ballerinalang.model.values.BByte; import org.ballerinalang.model.values.BCallableFuture; import org.ballerinalang.model.values.BClosure; import org.ballerinalang.model.values.BDecimal; import org.ballerinalang.model.values.BError; import org.ballerinalang.model.values.BFloat; import org.ballerinalang.model.values.BFunctionPointer; import org.ballerinalang.model.values.BFuture; import org.ballerinalang.model.values.BIntRange; import org.ballerinalang.model.values.BInteger; import org.ballerinalang.model.values.BIterator; import org.ballerinalang.model.values.BMap; import org.ballerinalang.model.values.BNewArray; import org.ballerinalang.model.values.BRefType; import org.ballerinalang.model.values.BStream; import org.ballerinalang.model.values.BString; import org.ballerinalang.model.values.BTable; import org.ballerinalang.model.values.BTypeDescValue; import org.ballerinalang.model.values.BValue; import org.ballerinalang.model.values.BValueArray; import org.ballerinalang.model.values.BValueType; import org.ballerinalang.model.values.BXML; import org.ballerinalang.model.values.BXMLAttributes; import org.ballerinalang.model.values.BXMLQName; import org.ballerinalang.model.values.BXMLSequence; import org.ballerinalang.util.FunctionFlags; import org.ballerinalang.util.Transactions; import org.ballerinalang.util.codegen.CallableUnitInfo; import org.ballerinalang.util.codegen.ErrorTableEntry; import org.ballerinalang.util.codegen.FunctionInfo; import org.ballerinalang.util.codegen.Instruction; import org.ballerinalang.util.codegen.Instruction.InstructionCALL; import org.ballerinalang.util.codegen.Instruction.InstructionIteratorNext; import org.ballerinalang.util.codegen.Instruction.InstructionLock; import org.ballerinalang.util.codegen.Instruction.InstructionTrBegin; import org.ballerinalang.util.codegen.Instruction.InstructionTrEnd; import org.ballerinalang.util.codegen.Instruction.InstructionTrRetry; import org.ballerinalang.util.codegen.Instruction.InstructionUnLock; import org.ballerinalang.util.codegen.Instruction.InstructionVCALL; import org.ballerinalang.util.codegen.Instruction.InstructionWRKSendReceive; import org.ballerinalang.util.codegen.InstructionCodes; import org.ballerinalang.util.codegen.LineNumberInfo; import org.ballerinalang.util.codegen.ObjectTypeInfo; import org.ballerinalang.util.codegen.StructFieldInfo; import org.ballerinalang.util.codegen.StructureTypeInfo; import org.ballerinalang.util.codegen.TypeDefInfo; import org.ballerinalang.util.codegen.WorkerDataChannelInfo; import org.ballerinalang.util.codegen.attributes.AttributeInfo; import org.ballerinalang.util.codegen.attributes.AttributeInfoPool; import org.ballerinalang.util.codegen.attributes.DefaultValueAttributeInfo; import org.ballerinalang.util.codegen.cpentries.BlobCPEntry; import org.ballerinalang.util.codegen.cpentries.FloatCPEntry; import org.ballerinalang.util.codegen.cpentries.FunctionCallCPEntry; import org.ballerinalang.util.codegen.cpentries.FunctionRefCPEntry; import org.ballerinalang.util.codegen.cpentries.IntegerCPEntry; import org.ballerinalang.util.codegen.cpentries.StringCPEntry; import org.ballerinalang.util.codegen.cpentries.StructureRefCPEntry; import org.ballerinalang.util.codegen.cpentries.TypeRefCPEntry; import org.ballerinalang.util.codegen.cpentries.UTF8CPEntry; import org.ballerinalang.util.debugger.DebugContext; import org.ballerinalang.util.debugger.Debugger; import org.ballerinalang.util.exceptions.BLangExceptionHelper; import org.ballerinalang.util.exceptions.BLangFreezeException; import org.ballerinalang.util.exceptions.BLangMapStoreException; import org.ballerinalang.util.exceptions.BLangNullReferenceException; import org.ballerinalang.util.exceptions.BallerinaErrorReasons; import org.ballerinalang.util.exceptions.BallerinaException; import org.ballerinalang.util.exceptions.RuntimeErrors; import org.ballerinalang.util.observability.ObserveUtils; import org.ballerinalang.util.program.BLangVMUtils; import org.ballerinalang.util.transactions.TransactionConstants; import org.ballerinalang.util.transactions.TransactionLocalContext; import org.ballerinalang.util.transactions.TransactionResourceManager; import org.ballerinalang.util.transactions.TransactionUtils; import org.wso2.ballerinalang.compiler.util.BArrayState; import java.math.BigDecimal; import java.math.MathContext; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.Set; import java.util.Stack; import java.util.UUID; import java.util.stream.Collectors; import java.util.stream.LongStream; import static org.ballerinalang.util.BLangConstants.BBYTE_MAX_VALUE; import static org.ballerinalang.util.BLangConstants.BBYTE_MIN_VALUE; import static org.ballerinalang.util.BLangConstants.BINT_MAX_VALUE_BIG_DECIMAL_RANGE_MAX; import static org.ballerinalang.util.BLangConstants.BINT_MAX_VALUE_DOUBLE_RANGE_MAX; import static org.ballerinalang.util.BLangConstants.BINT_MIN_VALUE_BIG_DECIMAL_RANGE_MIN; import static org.ballerinalang.util.BLangConstants.BINT_MIN_VALUE_DOUBLE_RANGE_MIN; import static org.ballerinalang.util.BLangConstants.STRING_NULL_VALUE; /** * This class executes Ballerina instruction codes by acting as a VM. * * @since 0.985.0 */ @SuppressWarnings({ "rawtypes", "unchecked" }) public class BVM { /** * This is to be used to continue the execution of a strand when a non blocking call continues. * * @param strand to be executed */ static void execute(Strand strand) { int i, j, k, l; int cpIndex; FunctionCallCPEntry funcCallCPEntry; FunctionRefCPEntry funcRefCPEntry; TypeRefCPEntry typeRefCPEntry; FunctionInfo functionInfo; InstructionCALL callIns; boolean debugEnabled = strand.programFile.getDebugger().isDebugEnabled(); int callersRetRegIndex; StackFrame sf = strand.currentFrame; while (sf.ip >= 0) { if (strand.aborted) { handleFutureTermination(strand); return; } if (debugEnabled && debug(strand)) { return; } Instruction instruction = sf.code[sf.ip]; int opcode = instruction.getOpcode(); int[] operands = instruction.getOperands(); sf.ip++; switch (opcode) { case InstructionCodes.ICONST: cpIndex = operands[0]; i = operands[1]; sf.longRegs[i] = ((IntegerCPEntry) sf.constPool[cpIndex]).getValue(); break; case InstructionCodes.FCONST: cpIndex = operands[0]; i = operands[1]; sf.doubleRegs[i] = ((FloatCPEntry) sf.constPool[cpIndex]).getValue(); break; case InstructionCodes.DCONST: cpIndex = operands[0]; i = operands[1]; String decimalVal = ((UTF8CPEntry) sf.constPool[cpIndex]).getValue(); sf.refRegs[i] = new BDecimal(new BigDecimal(decimalVal, MathContext.DECIMAL128)); break; case InstructionCodes.SCONST: cpIndex = operands[0]; i = operands[1]; sf.stringRegs[i] = ((StringCPEntry) sf.constPool[cpIndex]).getValue(); break; case InstructionCodes.ICONST_0: i = operands[0]; sf.longRegs[i] = 0; break; case InstructionCodes.ICONST_1: i = operands[0]; sf.longRegs[i] = 1; break; case InstructionCodes.ICONST_2: i = operands[0]; sf.longRegs[i] = 2; break; case InstructionCodes.ICONST_3: i = operands[0]; sf.longRegs[i] = 3; break; case InstructionCodes.ICONST_4: i = operands[0]; sf.longRegs[i] = 4; break; case InstructionCodes.ICONST_5: i = operands[0]; sf.longRegs[i] = 5; break; case InstructionCodes.FCONST_0: i = operands[0]; sf.doubleRegs[i] = 0; break; case InstructionCodes.FCONST_1: i = operands[0]; sf.doubleRegs[i] = 1; break; case InstructionCodes.FCONST_2: i = operands[0]; sf.doubleRegs[i] = 2; break; case InstructionCodes.FCONST_3: i = operands[0]; sf.doubleRegs[i] = 3; break; case InstructionCodes.FCONST_4: i = operands[0]; sf.doubleRegs[i] = 4; break; case InstructionCodes.FCONST_5: i = operands[0]; sf.doubleRegs[i] = 5; break; case InstructionCodes.BCONST_0: i = operands[0]; sf.intRegs[i] = 0; break; case InstructionCodes.BCONST_1: i = operands[0]; sf.intRegs[i] = 1; break; case InstructionCodes.RCONST_NULL: i = operands[0]; sf.refRegs[i] = null; break; case InstructionCodes.BACONST: cpIndex = operands[0]; i = operands[1]; sf.refRegs[i] = new BValueArray(((BlobCPEntry) sf.constPool[cpIndex]).getValue()); break; case InstructionCodes.IMOVE: case InstructionCodes.FMOVE: case InstructionCodes.SMOVE: case InstructionCodes.BMOVE: case InstructionCodes.RMOVE: case InstructionCodes.IALOAD: case InstructionCodes.BIALOAD: case InstructionCodes.FALOAD: case InstructionCodes.SALOAD: case InstructionCodes.BALOAD: case InstructionCodes.RALOAD: case InstructionCodes.JSONALOAD: case InstructionCodes.IGLOAD: case InstructionCodes.FGLOAD: case InstructionCodes.SGLOAD: case InstructionCodes.BGLOAD: case InstructionCodes.RGLOAD: case InstructionCodes.MAPLOAD: case InstructionCodes.JSONLOAD: execLoadOpcodes(strand, sf, opcode, operands); break; case InstructionCodes.IASTORE: case InstructionCodes.BIASTORE: case InstructionCodes.FASTORE: case InstructionCodes.SASTORE: case InstructionCodes.BASTORE: case InstructionCodes.RASTORE: case InstructionCodes.JSONASTORE: case InstructionCodes.IGSTORE: case InstructionCodes.FGSTORE: case InstructionCodes.SGSTORE: case InstructionCodes.BGSTORE: case InstructionCodes.RGSTORE: case InstructionCodes.MAPSTORE: case InstructionCodes.JSONSTORE: execStoreOpcodes(strand, sf, opcode, operands); break; case InstructionCodes.IADD: case InstructionCodes.FADD: case InstructionCodes.SADD: case InstructionCodes.DADD: case InstructionCodes.XMLADD: case InstructionCodes.ISUB: case InstructionCodes.FSUB: case InstructionCodes.DSUB: case InstructionCodes.IMUL: case InstructionCodes.FMUL: case InstructionCodes.DMUL: case InstructionCodes.IDIV: case InstructionCodes.FDIV: case InstructionCodes.DDIV: case InstructionCodes.IMOD: case InstructionCodes.FMOD: case InstructionCodes.DMOD: case InstructionCodes.INEG: case InstructionCodes.FNEG: case InstructionCodes.DNEG: case InstructionCodes.BNOT: case InstructionCodes.IEQ: case InstructionCodes.FEQ: case InstructionCodes.SEQ: case InstructionCodes.BEQ: case InstructionCodes.DEQ: case InstructionCodes.REQ: case InstructionCodes.REF_EQ: case InstructionCodes.TEQ: case InstructionCodes.INE: case InstructionCodes.FNE: case InstructionCodes.SNE: case InstructionCodes.BNE: case InstructionCodes.DNE: case InstructionCodes.RNE: case InstructionCodes.REF_NEQ: case InstructionCodes.TNE: case InstructionCodes.IAND: case InstructionCodes.IOR: case InstructionCodes.IXOR: case InstructionCodes.BILSHIFT: case InstructionCodes.BIRSHIFT: case InstructionCodes.IRSHIFT: case InstructionCodes.ILSHIFT: case InstructionCodes.IURSHIFT: case InstructionCodes.TYPE_TEST: case InstructionCodes.IS_LIKE: execBinaryOpCodes(strand, sf, opcode, operands); break; case InstructionCodes.TYPELOAD: cpIndex = operands[0]; j = operands[1]; TypeRefCPEntry typeEntry = (TypeRefCPEntry) sf.constPool[cpIndex]; sf.refRegs[j] = new BTypeDescValue(typeEntry.getType()); break; case InstructionCodes.HALT: if (strand.fp > 0) { // Stop the observation context before popping the stack frame ObserveUtils.stopCallableObservation(strand); strand.popFrame(); break; } sf.ip = -1; strand.respCallback.signal(); break; case InstructionCodes.IGT: case InstructionCodes.FGT: case InstructionCodes.DGT: case InstructionCodes.IGE: case InstructionCodes.FGE: case InstructionCodes.DGE: case InstructionCodes.ILT: case InstructionCodes.FLT: case InstructionCodes.DLT: case InstructionCodes.ILE: case InstructionCodes.FLE: case InstructionCodes.DLE: case InstructionCodes.REQ_NULL: case InstructionCodes.RNE_NULL: case InstructionCodes.BR_TRUE: case InstructionCodes.BR_FALSE: case InstructionCodes.GOTO: execCmpAndBranchOpcodes(strand, sf, opcode, operands); break; case InstructionCodes.INT_RANGE: execIntegerRangeOpcodes(sf, operands); break; case InstructionCodes.TR_RETRY: InstructionTrRetry trRetry = (InstructionTrRetry) instruction; retryTransaction(strand, trRetry.blockId, trRetry.abortEndIp, trRetry.trStatusReg); break; case InstructionCodes.CALL: callIns = (InstructionCALL) instruction; strand = invokeCallable(strand, callIns.functionInfo, callIns.argRegs, callIns.retRegs[0], callIns.flags); if (strand == null) { return; } break; case InstructionCodes.VCALL: InstructionVCALL vcallIns = (InstructionVCALL) instruction; strand = invokeVirtualFunction(strand, sf, vcallIns.receiverRegIndex, vcallIns.functionInfo, vcallIns.argRegs, vcallIns.retRegs[0], vcallIns.flags); if (strand == null) { return; } break; case InstructionCodes.TR_BEGIN: InstructionTrBegin trBegin = (InstructionTrBegin) instruction; beginTransaction(strand, trBegin.transactionType, trBegin.blockId, trBegin.retryCountReg, trBegin.committedFuncIndex, trBegin.abortedFuncIndex); break; case InstructionCodes.TR_END: InstructionTrEnd trEnd = (InstructionTrEnd) instruction; endTransaction(strand, trEnd.blockId, trEnd.endType, trEnd.statusRegIndex, trEnd.errorRegIndex); break; case InstructionCodes.WRKSEND: InstructionWRKSendReceive wrkSendIns = (InstructionWRKSendReceive) instruction; handleWorkerSend(strand, wrkSendIns.dataChannelInfo, wrkSendIns.type, wrkSendIns.reg, wrkSendIns.channelInSameStrand); break; case InstructionCodes.WRKRECEIVE: InstructionWRKSendReceive wrkReceiveIns = (InstructionWRKSendReceive) instruction; if (!handleWorkerReceive(strand, wrkReceiveIns.dataChannelInfo, wrkReceiveIns.type, wrkReceiveIns.reg, wrkReceiveIns.channelInSameStrand)) { return; } break; case InstructionCodes.CHNRECEIVE: Instruction.InstructionCHNReceive chnReceiveIns = (Instruction.InstructionCHNReceive) instruction; if (!handleCHNReceive(strand, chnReceiveIns.channelName, chnReceiveIns.receiverType, chnReceiveIns.receiverReg, chnReceiveIns.keyType, chnReceiveIns.keyReg)) { return; } break; case InstructionCodes.CHNSEND: Instruction.InstructionCHNSend chnSendIns = (Instruction.InstructionCHNSend) instruction; handleCHNSend(strand, chnSendIns.channelName, chnSendIns.dataType, chnSendIns.dataReg, chnSendIns.keyType, chnSendIns.keyReg); break; case InstructionCodes.FLUSH: Instruction.InstructionFlush flushIns = (Instruction.InstructionFlush) instruction; if (!WaitCallbackHandler.handleFlush(strand, flushIns.retReg, flushIns.channels)) { return; } break; case InstructionCodes.WORKERSYNCSEND: Instruction.InstructionWRKSyncSend syncSendIns = (Instruction.InstructionWRKSyncSend) instruction; if (!handleWorkerSyncSend(strand, syncSendIns.dataChannelInfo, syncSendIns.type, syncSendIns.reg, syncSendIns.retReg, syncSendIns.isSameStrand)) { return; } //worker data channel will resume this upon data retrieval or error break; case InstructionCodes.PANIC: i = operands[0]; if (i >= 0) { BError error = (BError) sf.refRegs[i]; if (error == null) { //TODO do we need this null check? handleNullRefError(strand); break; } strand.setError(error); } handleError(strand); break; case InstructionCodes.ERROR: createNewError(operands, strand, sf); break; case InstructionCodes.FPCALL: i = operands[0]; if (sf.refRegs[i] == null) { handleNullRefError(strand); break; } cpIndex = operands[1]; funcCallCPEntry = (FunctionCallCPEntry) sf.constPool[cpIndex]; functionInfo = ((BFunctionPointer) sf.refRegs[i]).value(); strand = invokeCallable(strand, (BFunctionPointer) sf.refRegs[i], funcCallCPEntry, functionInfo, sf, funcCallCPEntry.getFlags()); if (strand == null) { return; } break; case InstructionCodes.FPLOAD: i = operands[0]; j = operands[1]; k = operands[2]; funcRefCPEntry = (FunctionRefCPEntry) sf.constPool[i]; typeEntry = (TypeRefCPEntry) sf.constPool[k]; BFunctionPointer functionPointer = new BFunctionPointer(funcRefCPEntry.getFunctionInfo(), typeEntry.getType()); sf.refRegs[j] = functionPointer; findAndAddAdditionalVarRegIndexes(sf, operands, functionPointer); break; case InstructionCodes.VFPLOAD: i = operands[0]; j = operands[1]; k = operands[2]; int m = operands[5]; funcRefCPEntry = (FunctionRefCPEntry) sf.constPool[i]; typeEntry = (TypeRefCPEntry) sf.constPool[k]; BMap<String, BValue> structVal = (BMap<String, BValue>) sf.refRegs[m]; if (structVal == null) { handleNullRefError(strand); break; } StructureTypeInfo structInfo = (ObjectTypeInfo) ((BStructureType) structVal.getType()) .getTypeInfo(); FunctionInfo attachedFuncInfo = structInfo.funcInfoEntries .get(funcRefCPEntry.getFunctionInfo().getName()); BFunctionPointer fPointer = new BFunctionPointer(attachedFuncInfo, typeEntry.getType()); sf.refRegs[j] = fPointer; findAndAddAdditionalVarRegIndexes(sf, operands, fPointer); break; case InstructionCodes.I2ANY: case InstructionCodes.BI2ANY: case InstructionCodes.F2ANY: case InstructionCodes.S2ANY: case InstructionCodes.B2ANY: case InstructionCodes.ANY2I: case InstructionCodes.ANY2BI: case InstructionCodes.ANY2F: case InstructionCodes.ANY2S: case InstructionCodes.ANY2B: case InstructionCodes.ANY2D: case InstructionCodes.ARRAY2JSON: case InstructionCodes.JSON2ARRAY: case InstructionCodes.ANY2JSON: case InstructionCodes.ANY2XML: case InstructionCodes.ANY2MAP: case InstructionCodes.ANY2TYPE: case InstructionCodes.ANY2E: case InstructionCodes.ANY2T: case InstructionCodes.ANY2C: case InstructionCodes.ANY2DT: case InstructionCodes.CHECKCAST: case InstructionCodes.IS_ASSIGNABLE: case InstructionCodes.O2JSON: case InstructionCodes.TYPE_CAST: execTypeCastOpcodes(strand, sf, opcode, operands); break; case InstructionCodes.I2F: case InstructionCodes.I2S: case InstructionCodes.I2B: case InstructionCodes.I2D: case InstructionCodes.I2BI: case InstructionCodes.F2BI: case InstructionCodes.D2BI: case InstructionCodes.F2I: case InstructionCodes.F2S: case InstructionCodes.F2B: case InstructionCodes.F2D: case InstructionCodes.S2I: case InstructionCodes.S2F: case InstructionCodes.S2B: case InstructionCodes.S2D: case InstructionCodes.B2I: case InstructionCodes.B2F: case InstructionCodes.B2S: case InstructionCodes.B2D: case InstructionCodes.D2I: case InstructionCodes.D2F: case InstructionCodes.D2S: case InstructionCodes.D2B: case InstructionCodes.DT2XML: case InstructionCodes.DT2JSON: case InstructionCodes.T2MAP: case InstructionCodes.T2JSON: case InstructionCodes.MAP2JSON: case InstructionCodes.JSON2MAP: case InstructionCodes.MAP2T: case InstructionCodes.JSON2T: case InstructionCodes.XMLATTRS2MAP: case InstructionCodes.XML2S: case InstructionCodes.ANY2SCONV: execTypeConversionOpcodes(strand, sf, opcode, operands); break; case InstructionCodes.INEWARRAY: i = operands[0]; j = operands[2]; sf.refRegs[i] = new BValueArray(BTypes.typeInt, (int) sf.longRegs[j]); break; case InstructionCodes.BINEWARRAY: i = operands[0]; j = operands[2]; sf.refRegs[i] = new BValueArray(BTypes.typeByte, (int) sf.longRegs[j]); break; case InstructionCodes.FNEWARRAY: i = operands[0]; j = operands[2]; sf.refRegs[i] = new BValueArray(BTypes.typeFloat, (int) sf.longRegs[j]); break; case InstructionCodes.SNEWARRAY: i = operands[0]; j = operands[2]; sf.refRegs[i] = new BValueArray(BTypes.typeString, (int) sf.longRegs[j]); break; case InstructionCodes.BNEWARRAY: i = operands[0]; j = operands[2]; sf.refRegs[i] = new BValueArray(BTypes.typeBoolean, (int) sf.longRegs[j]); break; case InstructionCodes.RNEWARRAY: i = operands[0]; cpIndex = operands[1]; typeRefCPEntry = (TypeRefCPEntry) sf.constPool[cpIndex]; sf.refRegs[i] = new BValueArray(typeRefCPEntry.getType()); break; case InstructionCodes.NEWSTRUCT: createNewStruct(operands, sf); break; case InstructionCodes.NEWMAP: i = operands[0]; cpIndex = operands[1]; typeRefCPEntry = (TypeRefCPEntry) sf.constPool[cpIndex]; sf.refRegs[i] = new BMap<String, BRefType>(typeRefCPEntry.getType()); break; case InstructionCodes.NEWTABLE: i = operands[0]; cpIndex = operands[1]; j = operands[2]; k = operands[3]; l = operands[4]; typeRefCPEntry = (TypeRefCPEntry) sf.constPool[cpIndex]; BValueArray indexColumns = (BValueArray) sf.refRegs[j]; BValueArray keyColumns = (BValueArray) sf.refRegs[k]; BValueArray dataRows = (BValueArray) sf.refRegs[l]; try { sf.refRegs[i] = new BTable(typeRefCPEntry.getType(), indexColumns, keyColumns, dataRows); } catch (BallerinaException e) { strand.setError(BLangVMErrors.createError(strand, e.getMessage())); handleError(strand); } break; case InstructionCodes.NEWSTREAM: i = operands[0]; cpIndex = operands[1]; typeRefCPEntry = (TypeRefCPEntry) sf.constPool[cpIndex]; StringCPEntry name = (StringCPEntry) sf.constPool[operands[2]]; BStream stream = new BStream(typeRefCPEntry.getType(), name.getValue()); sf.refRegs[i] = stream; break; case InstructionCodes.IRET: j = operands[0]; if (strand.fp > 0) { StackFrame pf = strand.peekFrame(1); callersRetRegIndex = sf.retReg; pf.longRegs[callersRetRegIndex] = sf.longRegs[j]; } else { strand.respCallback.setIntReturn(sf.longRegs[j]); } break; case InstructionCodes.FRET: j = operands[0]; if (strand.fp > 0) { StackFrame pf = strand.peekFrame(1); callersRetRegIndex = sf.retReg; pf.doubleRegs[callersRetRegIndex] = sf.doubleRegs[j]; } else { strand.respCallback.setFloatReturn(sf.doubleRegs[j]); } break; case InstructionCodes.SRET: j = operands[0]; if (strand.fp > 0) { StackFrame pf = strand.peekFrame(1); callersRetRegIndex = sf.retReg; pf.stringRegs[callersRetRegIndex] = sf.stringRegs[j]; } else { strand.respCallback.setStringReturn(sf.stringRegs[j]); } break; case InstructionCodes.BRET: j = operands[0]; if (strand.fp > 0) { StackFrame pf = strand.peekFrame(1); callersRetRegIndex = sf.retReg; pf.intRegs[callersRetRegIndex] = sf.intRegs[j]; } else { strand.respCallback.setBooleanReturn(sf.intRegs[j]); } break; case InstructionCodes.DRET: case InstructionCodes.RRET: j = operands[0]; if (strand.fp > 0) { StackFrame pf = strand.peekFrame(1); callersRetRegIndex = sf.retReg; pf.refRegs[callersRetRegIndex] = sf.refRegs[j]; } else { strand.respCallback.setRefReturn(sf.refRegs[j]); } if (checkIsType(sf.refRegs[j], BTypes.typeError)) { sf.errorRetReg = j; } break; case InstructionCodes.RET: if (strand.fp > 0) { // Stop the observation context before popping the stack frame ObserveUtils.stopCallableObservation(strand); if (sf.errorRetReg > -1) { //notifying waiting workers sf.handleChannelError(sf.refRegs[sf.errorRetReg], strand.peekFrame(1).wdChannels); } strand.popFrame(); break; } if (sf.errorRetReg > -1) { //notifying waiting workers sf.handleChannelError(sf.refRegs[sf.errorRetReg], strand.respCallback.parentChannels); } sf.ip = -1; strand.respCallback.signal(); return; case InstructionCodes.XMLATTRSTORE: case InstructionCodes.XMLATTRLOAD: case InstructionCodes.XML2XMLATTRS: case InstructionCodes.S2QNAME: case InstructionCodes.NEWQNAME: case InstructionCodes.NEWXMLELEMENT: case InstructionCodes.NEWXMLCOMMENT: case InstructionCodes.NEWXMLTEXT: case InstructionCodes.NEWXMLPI: case InstructionCodes.XMLSEQSTORE: case InstructionCodes.XMLSEQLOAD: case InstructionCodes.XMLLOAD: case InstructionCodes.XMLLOADALL: case InstructionCodes.NEWXMLSEQ: execXMLOpcodes(strand, sf, opcode, operands); break; case InstructionCodes.ITR_NEXT: execIteratorOperation(strand, sf, instruction); break; case InstructionCodes.LOCK: InstructionLock instructionLock = (InstructionLock) instruction; if (!handleVariableLock(strand, instructionLock.types, instructionLock.pkgRefs, instructionLock.varRegs, instructionLock.fieldRegs, instructionLock.varCount, instructionLock.uuid)) { return; } break; case InstructionCodes.UNLOCK: InstructionUnLock instructionUnLock = (InstructionUnLock) instruction; handleVariableUnlock(strand, instructionUnLock.types, instructionUnLock.pkgRefs, instructionUnLock.varRegs, instructionUnLock.varCount, instructionUnLock.uuid, instructionUnLock.hasFieldVar); break; case InstructionCodes.WAIT: if (!execWait(strand, operands)) { return; } break; case InstructionCodes.WAITALL: if (!execWaitForAll(strand, operands)) { return; } break; default: throw new UnsupportedOperationException(); } sf = strand.currentFrame; } } private static void handleFutureTermination(Strand strand) { // Set error to strand and callback BError error = BLangVMErrors.createCancelledFutureError(strand); strand.setError(error); ((SafeStrandCallback) strand.respCallback).setErrorForCancelledFuture(error); // Make the ip of current frame to -1 strand.currentFrame.ip = -1; // Panic all stack frames in the strand panicStackFrame(strand); // Signal transactions for errors signalTransactionError(strand, StackFrame.TransactionParticipantType.REMOTE_PARTICIPANT); strand.respCallback.signal(); } private static void panicStackFrame(Strand strand) { if (strand.fp < 0) { return; } StackFrame poppedFrame = strand.popFrame(); // Stop observation ObserveUtils.stopObservation(poppedFrame.observerContext); // Panic channels in the current frame poppedFrame.handleChannelPanic(strand.getError(), poppedFrame.wdChannels); // Signal transactions for errors signalTransactionError(strand, poppedFrame.trxParticipant); panicStackFrame(strand); } private static boolean handleWorkerSyncSend(Strand strand, WorkerDataChannelInfo dataChannelInfo, BType type, int reg, int retReg, boolean isSameStrand) { BRefType val = extractValue(strand.currentFrame, type, reg); WorkerDataChannel dataChannel = getWorkerChannel(strand, dataChannelInfo.getChannelName(), isSameStrand); return dataChannel.syncSendData(val, strand, retReg); } private static Strand invokeCallable(Strand strand, CallableUnitInfo callableUnitInfo, int[] argRegs, int retReg, int flags) { //TODO refactor when worker info is removed from compiler StackFrame df = new StackFrame(callableUnitInfo.getPackageInfo(), callableUnitInfo, callableUnitInfo.getDefaultWorkerInfo().getCodeAttributeInfo(), retReg, flags, callableUnitInfo.workerSendInChannels); copyArgValues(strand.currentFrame, df, argRegs, callableUnitInfo.getParamTypes()); if (!FunctionFlags.isAsync(df.invocationFlags)) { try { strand.pushFrame(df); } catch (ArrayIndexOutOfBoundsException e) { // Need to decrement the frame pointer count. Otherwise ArrayIndexOutOfBoundsException will // be thrown from the popFrame() as well. strand.fp--; strand.setError(BLangVMErrors.createError(strand, BallerinaErrorReasons.STACK_OVERFLOW_ERROR, "stack overflow")); handleError(strand); return strand; } // Start observation after pushing the stack frame ObserveUtils.startCallableObservation(strand, df.invocationFlags); if (callableUnitInfo.isNative()) { return invokeNativeCallable(callableUnitInfo, strand, df, retReg, df.invocationFlags); } return strand; } SafeStrandCallback strandCallback = new SafeStrandCallback(callableUnitInfo.getRetParamTypes()[0], strand.currentFrame.wdChannels, callableUnitInfo.workerSendInChannels); Strand calleeStrand = new Strand(strand.programFile, callableUnitInfo.getName(), strand.globalProps, strandCallback); calleeStrand.pushFrame(df); // Start observation after pushing the stack frame ObserveUtils.startCallableObservation(calleeStrand, strand.respCallback.getObserverContext()); if (callableUnitInfo.isNative()) { Context nativeCtx = new NativeCallContext(calleeStrand, callableUnitInfo, df); NativeCallableUnit nativeCallable = callableUnitInfo.getNativeCallableUnit(); if (nativeCallable.isBlocking()) { BVMScheduler.scheduleNative(nativeCallable, nativeCtx, null); } else { BLangCallableUnitCallback callableUnitCallback = new BLangCallableUnitCallback(nativeCtx, calleeStrand, retReg, callableUnitInfo.getRetParamTypes()[0]); BVMScheduler.executeNative(nativeCallable, nativeCtx, callableUnitCallback); } } else { BVMScheduler.schedule(calleeStrand); } strand.currentFrame.refRegs[retReg] = new BCallableFuture(callableUnitInfo.getName(), calleeStrand); return strand; } private static Strand invokeNativeCallable(CallableUnitInfo callableUnitInfo, Strand strand, StackFrame sf, int retReg, int flags) { BType retType = callableUnitInfo.getRetParamTypes()[0]; Context ctx = new NativeCallContext(strand, callableUnitInfo, sf); NativeCallableUnit nativeCallable = callableUnitInfo.getNativeCallableUnit(); try { if (nativeCallable.isBlocking()) { nativeCallable.execute(ctx, null); if (strand.fp > 0) { // Stop the observation context before popping the stack frame ObserveUtils.stopCallableObservation(strand); if (BVM.checkIsType(ctx.getReturnValue(), BTypes.typeError)) { strand.currentFrame.handleChannelError((BRefType) ctx.getReturnValue(), strand.peekFrame(1).wdChannels); } strand.popFrame(); StackFrame retFrame = strand.currentFrame; BLangVMUtils.populateWorkerDataWithValues(retFrame, retReg, ctx.getReturnValue(), retType); return strand; } if (BVM.checkIsType(ctx.getReturnValue(), BTypes.typeError)) { strand.currentFrame.handleChannelError((BRefType) ctx.getReturnValue(), strand.respCallback.parentChannels); } strand.respCallback.signal(); return null; } CallableUnitCallback callback = new BLangCallableUnitCallback(ctx, strand, retReg, retType); nativeCallable.execute(ctx, callback); return null; } catch (BLangNullReferenceException e) { strand.setError(BLangVMErrors.createNullRefException(strand)); } catch (BallerinaException e) { strand.setError(BLangVMErrors.createError(strand, e.getMessage(), e.getDetail())); } catch (Throwable e) { strand.setError(BLangVMErrors.createError(strand, e.getMessage())); } // Stop the observation context before popping the stack frame ObserveUtils.stopCallableObservation(strand); if (strand.fp > 0) { strand.currentFrame.handleChannelPanic(strand.getError(), strand.peekFrame(1).wdChannels); strand.popFrame(); } else { strand.currentFrame.handleChannelPanic(strand.getError(), strand.respCallback.parentChannels); strand.popFrame(); } handleError(strand); return strand; } private static void copyArgValues(StackFrame caller, StackFrame callee, int[] argRegs, BType[] paramTypes) { int longRegIndex = -1; int doubleRegIndex = -1; int stringRegIndex = -1; int booleanRegIndex = -1; int refRegIndex = -1; for (int i = 0; i < argRegs.length; i++) { BType paramType = paramTypes[i]; int argReg = argRegs[i]; switch (paramType.getTag()) { case TypeTags.INT_TAG: case TypeTags.BYTE_TAG: callee.longRegs[++longRegIndex] = caller.longRegs[argReg]; break; case TypeTags.FLOAT_TAG: callee.doubleRegs[++doubleRegIndex] = caller.doubleRegs[argReg]; break; case TypeTags.STRING_TAG: callee.stringRegs[++stringRegIndex] = caller.stringRegs[argReg]; break; case TypeTags.BOOLEAN_TAG: callee.intRegs[++booleanRegIndex] = caller.intRegs[argReg]; break; default: callee.refRegs[++refRegIndex] = caller.refRegs[argReg]; } } } private static void createNewError(int[] operands, Strand strand, StackFrame sf) { int i = operands[0]; int j = operands[1]; int k = operands[2]; int l = operands[3]; TypeRefCPEntry typeRefCPEntry = (TypeRefCPEntry) sf.constPool[i]; sf.refRegs[l] = (BRefType<?>) BLangVMErrors.createError(strand, true, (BErrorType) typeRefCPEntry.getType(), sf.stringRegs[j], (BMap<String, BValue>) sf.refRegs[k]); } /** * Handle sending a message to a channel. If there is a worker already waiting to accept this message, it is * resumed. * * @param ctx Current worker context * @param channelName Name os the channel to get the message * @param dataType Type od the message * @param dataReg Registry location of the message * @param keyType Type of message key * @param keyReg message key registry index */ private static void handleCHNSend(Strand ctx, String channelName, BType dataType, int dataReg, BType keyType, int keyReg) { BRefType keyVal = null; if (keyType != null) { keyVal = extractValue(ctx.currentFrame, keyType, keyReg); } BRefType dataVal = extractValue(ctx.currentFrame, dataType, dataReg); ChannelRegistry.PendingContext pendingCtx = ChannelManager.channelSenderAction(channelName, keyVal, dataVal, keyType, dataType); if (pendingCtx != null) { //inject the value to the ctx copyArgValueForWorkerReceive(pendingCtx.context.currentFrame, pendingCtx.regIndex, dataType, dataVal); BVMScheduler.schedule(pendingCtx.context); } } /** * Handles message receiving using a channel. * If the expected message is already available, it is assigned to the receiver reg and returns true. * * @param ctx Current worker context * @param channelName Name os the channel to get the message * @param receiverType Type of the expected message * @param receiverReg Registry index of the receiving message * @param keyType Type of message key * @param keyIndex message key registry index * @return true if a matching value is available */ private static boolean handleCHNReceive(Strand ctx, String channelName, BType receiverType, int receiverReg, BType keyType, int keyIndex) { BValue keyVal = null; if (keyType != null) { keyVal = extractValue(ctx.currentFrame, keyType, keyIndex); } BValue value = ChannelManager.channelReceiverAction(channelName, keyVal, keyType, ctx, receiverReg, receiverType); if (value != null) { copyArgValueForWorkerReceive(ctx.currentFrame, receiverReg, receiverType, (BRefType) value); return true; } return false; } private static Strand invokeCallable(Strand ctx, BFunctionPointer fp, FunctionCallCPEntry funcCallCPEntry, FunctionInfo functionInfo, StackFrame sf, int flags) { List<BClosure> closureVars = fp.getClosureVars(); int[] argRegs = funcCallCPEntry.getArgRegs(); if (closureVars.isEmpty()) { return invokeCallable(ctx, functionInfo, argRegs, funcCallCPEntry.getRetRegs()[0], flags); } int[] newArgRegs = new int[argRegs.length + closureVars.size()]; System.arraycopy(argRegs, 0, newArgRegs, closureVars.size(), argRegs.length); int argRegIndex = 0; int longIndex = expandLongRegs(sf, fp); int doubleIndex = expandDoubleRegs(sf, fp); int intIndex = expandIntRegs(sf, fp); int stringIndex = expandStringRegs(sf, fp); int refIndex = expandRefRegs(sf, fp); for (BClosure closure : closureVars) { switch (closure.getType().getTag()) { case TypeTags.INT_TAG: { sf.longRegs[longIndex] = ((BInteger) closure.value()).intValue(); newArgRegs[argRegIndex++] = longIndex++; break; } case TypeTags.BYTE_TAG: { sf.longRegs[longIndex] = ((BByte) closure.value()).byteValue(); newArgRegs[argRegIndex++] = longIndex++; break; } case TypeTags.FLOAT_TAG: { sf.doubleRegs[doubleIndex] = ((BFloat) closure.value()).floatValue(); newArgRegs[argRegIndex++] = doubleIndex++; break; } case TypeTags.BOOLEAN_TAG: { sf.intRegs[intIndex] = ((BBoolean) closure.value()).booleanValue() ? 1 : 0; newArgRegs[argRegIndex++] = intIndex++; break; } case TypeTags.STRING_TAG: { sf.stringRegs[stringIndex] = (closure.value()).stringValue(); newArgRegs[argRegIndex++] = stringIndex++; break; } default: sf.refRegs[refIndex] = ((BRefType<?>) closure.value()); newArgRegs[argRegIndex++] = refIndex++; } } return invokeCallable(ctx, functionInfo, newArgRegs, funcCallCPEntry.getRetRegs()[0], flags); } private static int expandLongRegs(StackFrame sf, BFunctionPointer fp) { int longIndex = 0; if (fp.getAdditionalIndexCount(BTypes.typeInt.getTag()) > 0) { if (sf.longRegs == null) { sf.longRegs = new long[0]; } long[] newLongRegs = new long[sf.longRegs.length + fp.getAdditionalIndexCount(BTypes.typeInt.getTag())]; System.arraycopy(sf.longRegs, 0, newLongRegs, 0, sf.longRegs.length); longIndex = sf.longRegs.length; sf.longRegs = newLongRegs; } return longIndex; } private static int expandIntRegs(StackFrame sf, BFunctionPointer fp) { int intIndex = 0; if (fp.getAdditionalIndexCount(BTypes.typeBoolean.getTag()) > 0) { if (sf.intRegs == null) { sf.intRegs = new int[0]; } int[] newIntRegs = new int[sf.intRegs.length + fp.getAdditionalIndexCount(BTypes.typeBoolean.getTag())]; System.arraycopy(sf.intRegs, 0, newIntRegs, 0, sf.intRegs.length); intIndex = sf.intRegs.length; sf.intRegs = newIntRegs; } return intIndex; } private static int expandDoubleRegs(StackFrame sf, BFunctionPointer fp) { int doubleIndex = 0; if (fp.getAdditionalIndexCount(BTypes.typeFloat.getTag()) > 0) { if (sf.doubleRegs == null) { sf.doubleRegs = new double[0]; } double[] newDoubleRegs = new double[sf.doubleRegs.length + fp.getAdditionalIndexCount(BTypes.typeFloat.getTag())]; System.arraycopy(sf.doubleRegs, 0, newDoubleRegs, 0, sf.doubleRegs.length); doubleIndex = sf.doubleRegs.length; sf.doubleRegs = newDoubleRegs; } return doubleIndex; } private static int expandStringRegs(StackFrame sf, BFunctionPointer fp) { int stringIndex = 0; if (fp.getAdditionalIndexCount(BTypes.typeString.getTag()) > 0) { if (sf.stringRegs == null) { sf.stringRegs = new String[0]; } String[] newStringRegs = new String[sf.stringRegs.length + fp.getAdditionalIndexCount(BTypes.typeString.getTag())]; System.arraycopy(sf.stringRegs, 0, newStringRegs, 0, sf.stringRegs.length); stringIndex = sf.stringRegs.length; sf.stringRegs = newStringRegs; } return stringIndex; } private static int expandRefRegs(StackFrame sf, BFunctionPointer fp) { int refIndex = 0; if (fp.getAdditionalIndexCount(BTypes.typeAny.getTag()) > 0) { if (sf.refRegs == null) { sf.refRegs = new BRefType[0]; } BRefType<?>[] newRefRegs = new BRefType[sf.refRegs.length + fp.getAdditionalIndexCount(BTypes.typeAny.getTag())]; System.arraycopy(sf.refRegs, 0, newRefRegs, 0, sf.refRegs.length); refIndex = sf.refRegs.length; sf.refRegs = newRefRegs; } return refIndex; } private static void findAndAddAdditionalVarRegIndexes(StackFrame sf, int[] operands, BFunctionPointer fp) { int h = operands[3]; //if '0', then there are no additional indexes needs to be processed if (h == 0) { return; } //or else, this is a closure related scenario for (int i = 0; i < h; i++) { int operandIndex = i + 4; int type = operands[operandIndex]; int index = operands[++operandIndex]; switch (type) { case TypeTags.INT_TAG: { fp.addClosureVar(new BClosure(new BInteger(sf.longRegs[index]), BTypes.typeInt), TypeTags.INT_TAG); break; } case TypeTags.BYTE_TAG: { // 'byte' values are stored in long registry and treated similar to 'int' values. // Hence, modifying the int index for byte as well. fp.addClosureVar(new BClosure(new BByte(sf.longRegs[index]), BTypes.typeByte), TypeTags.INT_TAG); break; } case TypeTags.FLOAT_TAG: { fp.addClosureVar(new BClosure(new BFloat(sf.doubleRegs[index]), BTypes.typeFloat), TypeTags.FLOAT_TAG); break; } case TypeTags.DECIMAL_TAG: { fp.addClosureVar(new BClosure(sf.refRegs[index], BTypes.typeDecimal), TypeTags.DECIMAL_TAG); break; } case TypeTags.BOOLEAN_TAG: { fp.addClosureVar(new BClosure(new BBoolean(sf.intRegs[index] == 1), BTypes.typeBoolean), TypeTags.BOOLEAN_TAG); break; } case TypeTags.STRING_TAG: { fp.addClosureVar(new BClosure(new BString(sf.stringRegs[index]), BTypes.typeString), TypeTags.STRING_TAG); break; } default: fp.addClosureVar(new BClosure(sf.refRegs[index], BTypes.typeAny), TypeTags.ANY_TAG); } i++; } } private static void execCmpAndBranchOpcodes(Strand ctx, StackFrame sf, int opcode, int[] operands) { int i; int j; int k; BDecimal lhsValue; BDecimal rhsValue; switch (opcode) { case InstructionCodes.IGT: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.longRegs[i] > sf.longRegs[j] ? 1 : 0; break; case InstructionCodes.FGT: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.doubleRegs[i] > sf.doubleRegs[j] ? 1 : 0; break; case InstructionCodes.DGT: i = operands[0]; j = operands[1]; k = operands[2]; lhsValue = (BDecimal) sf.refRegs[i]; rhsValue = (BDecimal) sf.refRegs[j]; sf.intRegs[k] = checkDecimalGreaterThan(lhsValue, rhsValue) ? 1 : 0; break; case InstructionCodes.IGE: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.longRegs[i] >= sf.longRegs[j] ? 1 : 0; break; case InstructionCodes.FGE: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.doubleRegs[i] >= sf.doubleRegs[j] ? 1 : 0; break; case InstructionCodes.DGE: i = operands[0]; j = operands[1]; k = operands[2]; lhsValue = (BDecimal) sf.refRegs[i]; rhsValue = (BDecimal) sf.refRegs[j]; sf.intRegs[k] = checkDecimalGreaterThanOrEqual(lhsValue, rhsValue) ? 1 : 0; break; case InstructionCodes.ILT: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.longRegs[i] < sf.longRegs[j] ? 1 : 0; break; case InstructionCodes.FLT: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.doubleRegs[i] < sf.doubleRegs[j] ? 1 : 0; break; case InstructionCodes.DLT: i = operands[0]; j = operands[1]; k = operands[2]; lhsValue = (BDecimal) sf.refRegs[i]; rhsValue = (BDecimal) sf.refRegs[j]; sf.intRegs[k] = checkDecimalGreaterThan(rhsValue, lhsValue) ? 1 : 0; break; case InstructionCodes.ILE: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.longRegs[i] <= sf.longRegs[j] ? 1 : 0; break; case InstructionCodes.FLE: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.doubleRegs[i] <= sf.doubleRegs[j] ? 1 : 0; break; case InstructionCodes.DLE: i = operands[0]; j = operands[1]; k = operands[2]; lhsValue = (BDecimal) sf.refRegs[i]; rhsValue = (BDecimal) sf.refRegs[j]; sf.intRegs[k] = checkDecimalGreaterThanOrEqual(rhsValue, lhsValue) ? 1 : 0; break; case InstructionCodes.REQ_NULL: i = operands[0]; j = operands[1]; if (sf.refRegs[i] == null) { sf.intRegs[j] = 1; } else { sf.intRegs[j] = 0; } break; case InstructionCodes.RNE_NULL: i = operands[0]; j = operands[1]; if (sf.refRegs[i] != null) { sf.intRegs[j] = 1; } else { sf.intRegs[j] = 0; } break; case InstructionCodes.BR_TRUE: i = operands[0]; j = operands[1]; if (sf.intRegs[i] == 1) { sf.ip = j; } break; case InstructionCodes.BR_FALSE: i = operands[0]; j = operands[1]; if (sf.intRegs[i] == 0) { sf.ip = j; } break; case InstructionCodes.GOTO: i = operands[0]; sf.ip = i; break; default: throw new UnsupportedOperationException(); } } private static void execIntegerRangeOpcodes(StackFrame sf, int[] operands) { int i = operands[0]; int j = operands[1]; int k = operands[2]; sf.refRegs[k] = new BValueArray(LongStream.rangeClosed(sf.longRegs[i], sf.longRegs[j]).toArray()); } private static void execLoadOpcodes(Strand ctx, StackFrame sf, int opcode, int[] operands) { int i; int j; int k; int pkgIndex; int lvIndex; // Index of the local variable BValueArray bValueArray; BMap<String, BRefType> bMap; switch (opcode) { case InstructionCodes.IMOVE: lvIndex = operands[0]; i = operands[1]; sf.longRegs[i] = sf.longRegs[lvIndex]; break; case InstructionCodes.FMOVE: lvIndex = operands[0]; i = operands[1]; sf.doubleRegs[i] = sf.doubleRegs[lvIndex]; break; case InstructionCodes.SMOVE: lvIndex = operands[0]; i = operands[1]; sf.stringRegs[i] = sf.stringRegs[lvIndex]; break; case InstructionCodes.BMOVE: lvIndex = operands[0]; i = operands[1]; sf.intRegs[i] = sf.intRegs[lvIndex]; break; case InstructionCodes.RMOVE: lvIndex = operands[0]; i = operands[1]; sf.refRegs[i] = sf.refRegs[lvIndex]; break; case InstructionCodes.IALOAD: i = operands[0]; j = operands[1]; k = operands[2]; bValueArray = Optional.of((BValueArray) sf.refRegs[i]).get(); try { sf.longRegs[k] = bValueArray.getInt(sf.longRegs[j]); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.BIALOAD: i = operands[0]; j = operands[1]; k = operands[2]; bValueArray = Optional.of((BValueArray) sf.refRegs[i]).get(); try { sf.longRegs[k] = Byte.toUnsignedLong(bValueArray.getByte(sf.longRegs[j])); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.FALOAD: i = operands[0]; j = operands[1]; k = operands[2]; bValueArray = Optional.of((BValueArray) sf.refRegs[i]).get(); try { sf.doubleRegs[k] = bValueArray.getFloat(sf.longRegs[j]); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.SALOAD: i = operands[0]; j = operands[1]; k = operands[2]; bValueArray = Optional.of((BValueArray) sf.refRegs[i]).get(); try { sf.stringRegs[k] = bValueArray.getString(sf.longRegs[j]); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.BALOAD: i = operands[0]; j = operands[1]; k = operands[2]; bValueArray = Optional.of((BValueArray) sf.refRegs[i]).get(); try { sf.intRegs[k] = bValueArray.getBoolean(sf.longRegs[j]); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.RALOAD: i = operands[0]; j = operands[1]; k = operands[2]; BNewArray bNewArray = Optional.of((BNewArray) sf.refRegs[i]).get(); try { sf.refRegs[k] = ListUtils.execListGetOperation(bNewArray, sf.longRegs[j]); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.JSONALOAD: i = operands[0]; j = operands[1]; k = operands[2]; try { sf.refRegs[k] = JSONUtils.getArrayElement(sf.refRegs[i], sf.longRegs[j]); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.IGLOAD: // package index pkgIndex = operands[0]; // package level variable index i = operands[1]; // Stack registry index j = operands[2]; sf.longRegs[j] = ctx.programFile.globalMemArea.getIntField(pkgIndex, i); break; case InstructionCodes.FGLOAD: pkgIndex = operands[0]; i = operands[1]; j = operands[2]; sf.doubleRegs[j] = ctx.programFile.globalMemArea.getFloatField(pkgIndex, i); break; case InstructionCodes.SGLOAD: pkgIndex = operands[0]; i = operands[1]; j = operands[2]; sf.stringRegs[j] = ctx.programFile.globalMemArea.getStringField(pkgIndex, i); break; case InstructionCodes.BGLOAD: pkgIndex = operands[0]; i = operands[1]; j = operands[2]; sf.intRegs[j] = ctx.programFile.globalMemArea.getBooleanField(pkgIndex, i); break; case InstructionCodes.RGLOAD: pkgIndex = operands[0]; i = operands[1]; j = operands[2]; sf.refRegs[j] = ctx.programFile.globalMemArea.getRefField(pkgIndex, i); break; case InstructionCodes.MAPLOAD: i = operands[0]; j = operands[1]; k = operands[2]; bMap = (BMap<String, BRefType>) sf.refRegs[i]; if (bMap == null) { handleNullRefError(ctx); break; } IntegerCPEntry exceptCPEntry = (IntegerCPEntry) sf.constPool[operands[3]]; boolean except = exceptCPEntry.getValue() == 1; try { sf.refRegs[k] = bMap.get(sf.stringRegs[j], except); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.JSONLOAD: i = operands[0]; j = operands[1]; k = operands[2]; sf.refRegs[k] = JSONUtils.getElement(sf.refRegs[i], sf.stringRegs[j]); break; default: throw new UnsupportedOperationException(); } } private static void execStoreOpcodes(Strand ctx, StackFrame sf, int opcode, int[] operands) { int i; int j; int k; int pkgIndex; BValueArray bValueArray; BMap<String, BRefType> bMap; switch (opcode) { case InstructionCodes.IASTORE: i = operands[0]; j = operands[1]; k = operands[2]; bValueArray = Optional.of((BValueArray) sf.refRegs[i]).get(); try { bValueArray.add(sf.longRegs[j], sf.longRegs[k]); } catch (BLangFreezeException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.BIASTORE: i = operands[0]; j = operands[1]; k = operands[2]; bValueArray = Optional.of((BValueArray) sf.refRegs[i]).get(); try { bValueArray.add(sf.longRegs[j], (byte) sf.longRegs[k]); } catch (BLangFreezeException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.FASTORE: i = operands[0]; j = operands[1]; k = operands[2]; bValueArray = Optional.of((BValueArray) sf.refRegs[i]).get(); try { bValueArray.add(sf.longRegs[j], sf.doubleRegs[k]); } catch (BLangFreezeException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.SASTORE: i = operands[0]; j = operands[1]; k = operands[2]; bValueArray = Optional.of((BValueArray) sf.refRegs[i]).get(); try { bValueArray.add(sf.longRegs[j], sf.stringRegs[k]); } catch (BLangFreezeException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.BASTORE: i = operands[0]; j = operands[1]; k = operands[2]; bValueArray = Optional.of((BValueArray) sf.refRegs[i]).get(); try { bValueArray.add(sf.longRegs[j], sf.intRegs[k]); } catch (BLangFreezeException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.RASTORE: i = operands[0]; j = operands[1]; k = operands[2]; BNewArray list = Optional.of((BNewArray) sf.refRegs[i]).get(); long index = sf.longRegs[j]; BRefType refReg = sf.refRegs[k]; BType elementType = null; if (list.getType().getTag() == TypeTags.ARRAY_TAG) { elementType = ((BArrayType) list.getType()).getElementType(); } else { BTupleType tupleType = (BTupleType) list.getType(); if (isTupleIndexWithinRange(tupleType, index)) { elementType = tupleType.getTupleTypes().get((int) index); } } if (elementType != null && !checkIsType(refReg, elementType)) { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.INHERENT_TYPE_VIOLATION_ERROR, BLangExceptionHelper.getErrorMessage(RuntimeErrors.INCOMPATIBLE_TYPE, elementType, (refReg != null) ? refReg.getType() : BTypes.typeNull))); handleError(ctx); break; } try { ListUtils.execListAddOperation(list, index, refReg); } catch (BLangFreezeException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.JSONASTORE: i = operands[0]; j = operands[1]; k = operands[2]; try { JSONUtils.setArrayElement(sf.refRegs[i], sf.longRegs[j], sf.refRegs[k]); } catch (BLangFreezeException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.IGSTORE: pkgIndex = operands[0]; // Stack reg index i = operands[1]; // Global var index j = operands[2]; ctx.programFile.globalMemArea.setIntField(pkgIndex, j, sf.longRegs[i]); break; case InstructionCodes.FGSTORE: pkgIndex = operands[0]; i = operands[1]; j = operands[2]; ctx.programFile.globalMemArea.setFloatField(pkgIndex, j, sf.doubleRegs[i]); break; case InstructionCodes.SGSTORE: pkgIndex = operands[0]; i = operands[1]; j = operands[2]; ctx.programFile.globalMemArea.setStringField(pkgIndex, j, sf.stringRegs[i]); break; case InstructionCodes.BGSTORE: pkgIndex = operands[0]; i = operands[1]; j = operands[2]; ctx.programFile.globalMemArea.setBooleanField(pkgIndex, j, sf.intRegs[i]); break; case InstructionCodes.RGSTORE: pkgIndex = operands[0]; i = operands[1]; j = operands[2]; ctx.programFile.globalMemArea.setRefField(pkgIndex, j, sf.refRegs[i]); break; case InstructionCodes.MAPSTORE: i = operands[0]; j = operands[1]; k = operands[2]; bMap = (BMap<String, BRefType>) sf.refRegs[i]; if (bMap == null) { handleNullRefError(ctx); break; } try { handleMapStore(ctx, bMap, sf.stringRegs[j], sf.refRegs[k]); } catch (BLangMapStoreException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; case InstructionCodes.JSONSTORE: i = operands[0]; j = operands[1]; k = operands[2]; try { JSONUtils.setElement(sf.refRegs[i], sf.stringRegs[j], sf.refRegs[k]); } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } catch (BLangFreezeException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } break; default: throw new UnsupportedOperationException(); } } private static boolean isTupleIndexWithinRange(BTupleType tuple, long index) { return index >= 0 && index < tuple.getTupleTypes().size(); } private static void execBinaryOpCodes(Strand ctx, StackFrame sf, int opcode, int[] operands) { int i; int j; int k; BDecimal lhsValue; BDecimal rhsValue; switch (opcode) { case InstructionCodes.IADD: i = operands[0]; j = operands[1]; k = operands[2]; sf.longRegs[k] = sf.longRegs[i] + sf.longRegs[j]; break; case InstructionCodes.FADD: i = operands[0]; j = operands[1]; k = operands[2]; sf.doubleRegs[k] = sf.doubleRegs[i] + sf.doubleRegs[j]; break; case InstructionCodes.SADD: i = operands[0]; j = operands[1]; k = operands[2]; sf.stringRegs[k] = sf.stringRegs[i] + sf.stringRegs[j]; break; case InstructionCodes.DADD: i = operands[0]; j = operands[1]; k = operands[2]; lhsValue = (BDecimal) sf.refRegs[i]; rhsValue = (BDecimal) sf.refRegs[j]; sf.refRegs[k] = lhsValue.add(rhsValue); break; case InstructionCodes.XMLADD: i = operands[0]; j = operands[1]; k = operands[2]; BXML lhsXMLVal = (BXML) sf.refRegs[i]; BXML rhsXMLVal = (BXML) sf.refRegs[j]; // Here it is assumed that a refType addition can only be a xml-concat. sf.refRegs[k] = XMLUtils.concatenate(lhsXMLVal, rhsXMLVal); break; case InstructionCodes.ISUB: i = operands[0]; j = operands[1]; k = operands[2]; sf.longRegs[k] = sf.longRegs[i] - sf.longRegs[j]; break; case InstructionCodes.FSUB: i = operands[0]; j = operands[1]; k = operands[2]; sf.doubleRegs[k] = sf.doubleRegs[i] - sf.doubleRegs[j]; break; case InstructionCodes.DSUB: i = operands[0]; j = operands[1]; k = operands[2]; lhsValue = (BDecimal) sf.refRegs[i]; rhsValue = (BDecimal) sf.refRegs[j]; sf.refRegs[k] = lhsValue.subtract(rhsValue); break; case InstructionCodes.IMUL: i = operands[0]; j = operands[1]; k = operands[2]; sf.longRegs[k] = sf.longRegs[i] * sf.longRegs[j]; break; case InstructionCodes.FMUL: i = operands[0]; j = operands[1]; k = operands[2]; sf.doubleRegs[k] = sf.doubleRegs[i] * sf.doubleRegs[j]; break; case InstructionCodes.DMUL: i = operands[0]; j = operands[1]; k = operands[2]; lhsValue = (BDecimal) sf.refRegs[i]; rhsValue = (BDecimal) sf.refRegs[j]; sf.refRegs[k] = lhsValue.multiply(rhsValue); break; case InstructionCodes.IDIV: i = operands[0]; j = operands[1]; k = operands[2]; if (sf.longRegs[j] == 0) { ctx.setError( BLangVMErrors.createError(ctx, BallerinaErrorReasons.DIVISION_BY_ZERO_ERROR, " / by zero")); handleError(ctx); break; } sf.longRegs[k] = sf.longRegs[i] / sf.longRegs[j]; break; case InstructionCodes.FDIV: i = operands[0]; j = operands[1]; k = operands[2]; sf.doubleRegs[k] = sf.doubleRegs[i] / sf.doubleRegs[j]; break; case InstructionCodes.DDIV: i = operands[0]; j = operands[1]; k = operands[2]; lhsValue = (BDecimal) sf.refRegs[i]; rhsValue = (BDecimal) sf.refRegs[j]; sf.refRegs[k] = lhsValue.divide(rhsValue); break; case InstructionCodes.IMOD: i = operands[0]; j = operands[1]; k = operands[2]; if (sf.longRegs[j] == 0) { ctx.setError( BLangVMErrors.createError(ctx, BallerinaErrorReasons.DIVISION_BY_ZERO_ERROR, " / by zero")); handleError(ctx); break; } sf.longRegs[k] = sf.longRegs[i] % sf.longRegs[j]; break; case InstructionCodes.FMOD: i = operands[0]; j = operands[1]; k = operands[2]; sf.doubleRegs[k] = sf.doubleRegs[i] % sf.doubleRegs[j]; break; case InstructionCodes.DMOD: i = operands[0]; j = operands[1]; k = operands[2]; lhsValue = (BDecimal) sf.refRegs[i]; rhsValue = (BDecimal) sf.refRegs[j]; sf.refRegs[k] = lhsValue.remainder(rhsValue); break; case InstructionCodes.INEG: i = operands[0]; j = operands[1]; sf.longRegs[j] = -sf.longRegs[i]; break; case InstructionCodes.FNEG: i = operands[0]; j = operands[1]; sf.doubleRegs[j] = -sf.doubleRegs[i]; break; case InstructionCodes.DNEG: i = operands[0]; j = operands[1]; BigDecimal value = ((BDecimal) sf.refRegs[i]).decimalValue(); sf.refRegs[j] = new BDecimal(value.negate()); break; case InstructionCodes.BNOT: i = operands[0]; j = operands[1]; sf.intRegs[j] = sf.intRegs[i] == 0 ? 1 : 0; break; case InstructionCodes.IEQ: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.longRegs[i] == sf.longRegs[j] ? 1 : 0; break; case InstructionCodes.FEQ: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.doubleRegs[i] == sf.doubleRegs[j] ? 1 : 0; break; case InstructionCodes.SEQ: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = StringUtils.isEqual(sf.stringRegs[i], sf.stringRegs[j]) ? 1 : 0; break; case InstructionCodes.BEQ: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.intRegs[i] == sf.intRegs[j] ? 1 : 0; break; case InstructionCodes.DEQ: i = operands[0]; j = operands[1]; k = operands[2]; lhsValue = (BDecimal) sf.refRegs[i]; rhsValue = (BDecimal) sf.refRegs[j]; sf.intRegs[k] = isDecimalRealNumber(lhsValue) && isDecimalRealNumber(rhsValue) && lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) == 0 ? 1 : 0; break; case InstructionCodes.REQ: i = operands[0]; j = operands[1]; k = operands[2]; if (sf.refRegs[i] == null) { sf.intRegs[k] = sf.refRegs[j] == null ? 1 : 0; } else { sf.intRegs[k] = isEqual(sf.refRegs[i], sf.refRegs[j], new ArrayList<>()) ? 1 : 0; } break; case InstructionCodes.REF_EQ: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = isReferenceEqual(sf.refRegs[i], sf.refRegs[j]) ? 1 : 0; break; case InstructionCodes.TEQ: i = operands[0]; j = operands[1]; k = operands[2]; if (sf.refRegs[i] == null || sf.refRegs[j] == null) { handleNullRefError(ctx); break; //TODO is this correct? } sf.intRegs[k] = sf.refRegs[i].equals(sf.refRegs[j]) ? 1 : 0; break; case InstructionCodes.INE: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.longRegs[i] != sf.longRegs[j] ? 1 : 0; break; case InstructionCodes.FNE: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.doubleRegs[i] != sf.doubleRegs[j] ? 1 : 0; break; case InstructionCodes.SNE: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = !StringUtils.isEqual(sf.stringRegs[i], sf.stringRegs[j]) ? 1 : 0; break; case InstructionCodes.BNE: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = sf.intRegs[i] != sf.intRegs[j] ? 1 : 0; break; case InstructionCodes.DNE: i = operands[0]; j = operands[1]; k = operands[2]; lhsValue = (BDecimal) sf.refRegs[i]; rhsValue = (BDecimal) sf.refRegs[j]; sf.intRegs[k] = !isDecimalRealNumber(lhsValue) || !isDecimalRealNumber(rhsValue) || lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) != 0 ? 1 : 0; break; case InstructionCodes.RNE: i = operands[0]; j = operands[1]; k = operands[2]; if (sf.refRegs[i] == null) { sf.intRegs[k] = (sf.refRegs[j] != null) ? 1 : 0; } else { sf.intRegs[k] = (!isEqual(sf.refRegs[i], sf.refRegs[j], new ArrayList<>())) ? 1 : 0; } break; case InstructionCodes.REF_NEQ: i = operands[0]; j = operands[1]; k = operands[2]; sf.intRegs[k] = isReferenceInequal(sf.refRegs[i], sf.refRegs[j]) ? 1 : 0; break; case InstructionCodes.TNE: i = operands[0]; j = operands[1]; k = operands[2]; if (sf.refRegs[i] == null || sf.refRegs[j] == null) { handleNullRefError(ctx); break; //TODO is this correct? } sf.intRegs[k] = (!sf.refRegs[i].equals(sf.refRegs[j])) ? 1 : 0; break; case InstructionCodes.IAND: i = operands[0]; j = operands[1]; k = operands[2]; sf.longRegs[k] = sf.longRegs[i] & sf.longRegs[j]; break; case InstructionCodes.IOR: i = operands[0]; j = operands[1]; k = operands[2]; sf.longRegs[k] = sf.longRegs[i] | sf.longRegs[j]; break; case InstructionCodes.IXOR: i = operands[0]; j = operands[1]; k = operands[2]; sf.longRegs[k] = sf.longRegs[i] ^ sf.longRegs[j]; break; case InstructionCodes.BILSHIFT: i = operands[0]; j = operands[1]; k = operands[2]; sf.longRegs[k] = Byte.toUnsignedLong((byte) (sf.longRegs[i] << sf.longRegs[j])); break; case InstructionCodes.BIRSHIFT: i = operands[0]; j = operands[1]; k = operands[2]; sf.longRegs[k] = Byte.toUnsignedLong((byte) ((byte) sf.longRegs[i] >> sf.longRegs[j])); break; case InstructionCodes.IRSHIFT: i = operands[0]; j = operands[1]; k = operands[2]; sf.longRegs[k] = sf.longRegs[i] >> sf.longRegs[j]; break; case InstructionCodes.ILSHIFT: i = operands[0]; j = operands[1]; k = operands[2]; sf.longRegs[k] = sf.longRegs[i] << sf.longRegs[j]; break; case InstructionCodes.IURSHIFT: i = operands[0]; j = operands[1]; k = operands[2]; sf.longRegs[k] = sf.longRegs[i] >>> sf.longRegs[j]; break; case InstructionCodes.TYPE_TEST: i = operands[0]; j = operands[1]; k = operands[2]; TypeRefCPEntry typeRefCPEntry = (TypeRefCPEntry) sf.constPool[j]; sf.intRegs[k] = checkIsType(sf.refRegs[i], typeRefCPEntry.getType()) ? 1 : 0; break; case InstructionCodes.IS_LIKE: i = operands[0]; j = operands[1]; k = operands[2]; typeRefCPEntry = (TypeRefCPEntry) sf.constPool[j]; sf.intRegs[k] = checkIsLikeType(sf.refRegs[i], typeRefCPEntry.getType()) ? 1 : 0; break; default: throw new UnsupportedOperationException(); } } private static void execXMLOpcodes(Strand ctx, StackFrame sf, int opcode, int[] operands) { int i; int j; int k; int localNameIndex; int uriIndex; int prefixIndex; BXML<?> xmlVal; BXMLQName xmlQName; switch (opcode) { case InstructionCodes.XMLATTRSTORE: i = operands[0]; j = operands[1]; k = operands[2]; xmlVal = Optional.of((BXML) sf.refRegs[i]).get(); xmlQName = Optional.of((BXMLQName) sf.refRegs[j]).get(); try { xmlVal.setAttribute(xmlQName.getLocalName(), xmlQName.getUri(), xmlQName.getPrefix(), sf.stringRegs[k]); } catch (BallerinaException e) { ctx.setError( BLangVMErrors.createError(ctx, BallerinaErrorReasons.XML_OPERATION_ERROR, e.getMessage())); handleError(ctx); } break; case InstructionCodes.XMLATTRLOAD: i = operands[0]; j = operands[1]; k = operands[2]; xmlVal = Optional.of((BXML) sf.refRegs[i]).get(); xmlQName = Optional.of((BXMLQName) sf.refRegs[j]).get(); sf.stringRegs[k] = xmlVal.getAttribute(xmlQName.getLocalName(), xmlQName.getUri(), xmlQName.getPrefix()); break; case InstructionCodes.XML2XMLATTRS: i = operands[0]; j = operands[1]; xmlVal = (BXML) sf.refRegs[i]; sf.refRegs[j] = new BXMLAttributes(xmlVal); break; case InstructionCodes.S2QNAME: i = operands[0]; j = operands[1]; k = operands[2]; String qNameStr = sf.stringRegs[i]; int parenEndIndex = qNameStr.indexOf('}'); if (qNameStr.startsWith("{") && parenEndIndex > 0) { sf.stringRegs[j] = qNameStr.substring(parenEndIndex + 1, qNameStr.length()); sf.stringRegs[k] = qNameStr.substring(1, parenEndIndex); } else { sf.stringRegs[j] = qNameStr; sf.stringRegs[k] = STRING_NULL_VALUE; } break; case InstructionCodes.NEWQNAME: localNameIndex = operands[0]; uriIndex = operands[1]; prefixIndex = operands[2]; i = operands[3]; String localname = sf.stringRegs[localNameIndex]; localname = StringEscapeUtils.escapeXml11(localname); String prefix = sf.stringRegs[prefixIndex]; prefix = StringEscapeUtils.escapeXml11(prefix); sf.refRegs[i] = new BXMLQName(localname, sf.stringRegs[uriIndex], prefix); break; case InstructionCodes.XMLSEQLOAD: i = operands[0]; j = operands[1]; k = operands[2]; xmlVal = Optional.of((BXML) sf.refRegs[i]).get(); long index = sf.longRegs[j]; try { sf.refRegs[k] = xmlVal.getItem(index); } catch (BallerinaException e) { ctx.setError( BLangVMErrors.createError(ctx, BallerinaErrorReasons.XML_OPERATION_ERROR, e.getMessage())); handleError(ctx); } break; case InstructionCodes.XMLLOAD: i = operands[0]; j = operands[1]; k = operands[2]; xmlVal = Optional.of((BXML) sf.refRegs[i]).get(); String qname = sf.stringRegs[j]; sf.refRegs[k] = xmlVal.children(qname); break; case InstructionCodes.XMLLOADALL: i = operands[0]; j = operands[1]; xmlVal = Optional.of((BXML) sf.refRegs[i]).get(); sf.refRegs[j] = xmlVal.children(); break; case InstructionCodes.NEWXMLELEMENT: case InstructionCodes.NEWXMLCOMMENT: case InstructionCodes.NEWXMLTEXT: case InstructionCodes.NEWXMLPI: case InstructionCodes.XMLSEQSTORE: case InstructionCodes.NEWXMLSEQ: execXMLCreationOpcodes(ctx, sf, opcode, operands); break; default: throw new UnsupportedOperationException(); } } private static void execTypeCastOpcodes(Strand ctx, StackFrame sf, int opcode, int[] operands) { int i; int j; int cpIndex; // Index of the constant pool BRefType bRefTypeValue; TypeRefCPEntry typeRefCPEntry; switch (opcode) { case InstructionCodes.TYPE_CAST: i = operands[0]; cpIndex = operands[1]; j = operands[2]; typeRefCPEntry = (TypeRefCPEntry) sf.constPool[cpIndex]; BType expectedType = typeRefCPEntry.getType(); bRefTypeValue = sf.refRegs[i]; if (bRefTypeValue == null) { if (checkIsType(BTypes.typeNull, expectedType, new ArrayList<>())) { sf.refRegs[j] = null; break; } ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.TYPE_CAST_ERROR, BLangExceptionHelper.getErrorMessage(RuntimeErrors.TYPE_CAST_ERROR, "()", expectedType))); handleError(ctx); } else if (checkIsType(bRefTypeValue, expectedType)) { switch (expectedType.getTag()) { case TypeTags.STRING_TAG: sf.stringRegs[j] = bRefTypeValue.stringValue(); break; case TypeTags.BOOLEAN_TAG: sf.intRegs[j] = ((BBoolean) bRefTypeValue).booleanValue() ? 1 : 0; break; case TypeTags.INT_TAG: sf.longRegs[j] = ((BInteger) bRefTypeValue).intValue(); break; case TypeTags.FLOAT_TAG: sf.doubleRegs[j] = ((BFloat) bRefTypeValue).floatValue(); break; case TypeTags.DECIMAL_TAG: sf.refRegs[j] = ((BDecimal) bRefTypeValue); break; case TypeTags.BYTE_TAG: sf.longRegs[j] = ((BByte) bRefTypeValue).byteValue(); break; default: sf.refRegs[j] = bRefTypeValue; } } else if (containsNumericType(expectedType)) { execNumericConversionOrCastOpCode(ctx, sf, expectedType, bRefTypeValue, j, false); } else { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.TYPE_CAST_ERROR, BLangExceptionHelper.getErrorMessage(RuntimeErrors.TYPE_CAST_ERROR, bRefTypeValue.getType(), (expectedType.getTag() == TypeTags.NULL_TAG ? "()" : expectedType)))); handleError(ctx); } break; case InstructionCodes.I2ANY: i = operands[0]; j = operands[1]; sf.refRegs[j] = new BInteger(sf.longRegs[i]); break; case InstructionCodes.BI2ANY: i = operands[0]; j = operands[1]; sf.refRegs[j] = new BByte(sf.longRegs[i]); break; case InstructionCodes.F2ANY: i = operands[0]; j = operands[1]; sf.refRegs[j] = new BFloat(sf.doubleRegs[i]); break; case InstructionCodes.S2ANY: i = operands[0]; j = operands[1]; sf.refRegs[j] = new BString(sf.stringRegs[i]); break; case InstructionCodes.B2ANY: i = operands[0]; j = operands[1]; sf.refRegs[j] = new BBoolean(sf.intRegs[i] == 1); break; case InstructionCodes.ANY2I: i = operands[0]; j = operands[1]; sf.longRegs[j] = ((BValueType) sf.refRegs[i]).intValue(); break; case InstructionCodes.ANY2BI: i = operands[0]; j = operands[1]; sf.longRegs[j] = ((BValueType) sf.refRegs[i]).byteValue(); break; case InstructionCodes.ANY2F: i = operands[0]; j = operands[1]; sf.doubleRegs[j] = ((BValueType) sf.refRegs[i]).floatValue(); break; case InstructionCodes.ANY2S: i = operands[0]; j = operands[1]; sf.stringRegs[j] = sf.refRegs[i].stringValue(); break; case InstructionCodes.ANY2B: i = operands[0]; j = operands[1]; sf.intRegs[j] = ((BBoolean) sf.refRegs[i]).booleanValue() ? 1 : 0; break; case InstructionCodes.ANY2D: i = operands[0]; j = operands[1]; sf.refRegs[j] = new BDecimal(((BValueType) sf.refRegs[i]).decimalValue()); break; case InstructionCodes.ANY2JSON: handleAnyToRefTypeCast(ctx, sf, operands, BTypes.typeJSON); break; case InstructionCodes.ANY2XML: handleAnyToRefTypeCast(ctx, sf, operands, BTypes.typeXML); break; case InstructionCodes.ANY2MAP: handleAnyToRefTypeCast(ctx, sf, operands, BTypes.typeMap); break; case InstructionCodes.ANY2TYPE: handleAnyToRefTypeCast(ctx, sf, operands, BTypes.typeDesc); break; case InstructionCodes.ANY2DT: handleAnyToRefTypeCast(ctx, sf, operands, BTypes.typeTable); break; case InstructionCodes.ANY2STM: handleAnyToRefTypeCast(ctx, sf, operands, BTypes.typeStream); break; case InstructionCodes.ANY2E: case InstructionCodes.ANY2T: case InstructionCodes.ANY2C: case InstructionCodes.CHECKCAST: i = operands[0]; cpIndex = operands[1]; j = operands[2]; typeRefCPEntry = (TypeRefCPEntry) sf.constPool[cpIndex]; bRefTypeValue = sf.refRegs[i]; if (checkCast(bRefTypeValue, typeRefCPEntry.getType())) { sf.refRegs[j] = bRefTypeValue; } else { handleTypeCastError(ctx, sf, j, bRefTypeValue != null ? bRefTypeValue.getType() : BTypes.typeNull, typeRefCPEntry.getType()); } break; case InstructionCodes.IS_ASSIGNABLE: i = operands[0]; cpIndex = operands[1]; j = operands[2]; typeRefCPEntry = (TypeRefCPEntry) sf.constPool[cpIndex]; bRefTypeValue = sf.refRegs[i]; if (checkIsType(bRefTypeValue, typeRefCPEntry.getType())) { sf.intRegs[j] = 1; } else { sf.intRegs[j] = 0; } break; case InstructionCodes.ARRAY2JSON: convertArrayToJSON(ctx, operands, sf); break; case InstructionCodes.JSON2ARRAY: convertJSONToArray(ctx, operands, sf); break; case InstructionCodes.O2JSON: i = operands[0]; cpIndex = operands[1]; j = operands[2]; BJSONType targetType = (BJSONType) ((TypeRefCPEntry) sf.constPool[cpIndex]).getType(); bRefTypeValue = sf.refRegs[i]; sf.refRegs[j] = JSONUtils.convertUnionTypeToJSON(bRefTypeValue, targetType); break; default: throw new UnsupportedOperationException(); } } private static void execNumericConversionOrCastOpCode(Strand ctx, StackFrame sf, BType targetType, BRefType bRefTypeValue, int regIndex, boolean isRefTypeTarget) { BType sourceType = bRefTypeValue.getType(); int targetTag = targetType.getTag(); if ((targetTag == TypeTags.UNION_TAG && ((BUnionType) targetType).getMemberTypes().stream().filter(BVM::isBasicNumericType).count() > 1) || !isBasicNumericType(sourceType)) { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.TYPE_CAST_ERROR, BLangExceptionHelper.getErrorMessage(RuntimeErrors.TYPE_CAST_ERROR, sourceType, targetType))); handleError(ctx); return; } try { switch (targetTag) { case TypeTags.FLOAT_TAG: if (isRefTypeTarget) { sf.refRegs[regIndex] = new BFloat(((BValueType) bRefTypeValue).floatValue()); } else { sf.doubleRegs[regIndex] = ((BValueType) bRefTypeValue).floatValue(); } break; case TypeTags.DECIMAL_TAG: sf.refRegs[regIndex] = new BDecimal(((BValueType) bRefTypeValue).decimalValue()); break; case TypeTags.INT_TAG: if (isRefTypeTarget) { sf.refRegs[regIndex] = new BInteger(((BValueType) bRefTypeValue).intValue()); } else { sf.longRegs[regIndex] = ((BValueType) bRefTypeValue).intValue(); } break; case TypeTags.BYTE_TAG: if (isRefTypeTarget) { sf.refRegs[regIndex] = new BByte(((BValueType) bRefTypeValue).byteValue()); } else { sf.longRegs[regIndex] = ((BValueType) bRefTypeValue).byteValue(); } break; default: BType targetNumericType = ((BUnionType) targetType).getMemberTypes().stream() .filter(BVM::isBasicNumericType).findFirst().get(); // wouldn't get here if not present execNumericConversionOrCastOpCode(ctx, sf, targetNumericType, bRefTypeValue, regIndex, true); } } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } } private static void execTypeConversionOpcodes(Strand ctx, StackFrame sf, int opcode, int[] operands) { int i; int j; BRefType bRefType; String str; switch (opcode) { case InstructionCodes.I2F: i = operands[0]; j = operands[1]; sf.doubleRegs[j] = sf.longRegs[i]; break; case InstructionCodes.I2S: i = operands[0]; j = operands[1]; sf.stringRegs[j] = Long.toString(sf.longRegs[i]); break; case InstructionCodes.I2B: i = operands[0]; j = operands[1]; sf.intRegs[j] = sf.longRegs[i] != 0 ? 1 : 0; break; case InstructionCodes.I2D: i = operands[0]; j = operands[1]; sf.refRegs[j] = new BDecimal((new BigDecimal(sf.longRegs[i], MathContext.DECIMAL128)).setScale(1, BigDecimal.ROUND_HALF_EVEN)); break; case InstructionCodes.I2BI: i = operands[0]; j = operands[1]; if (!isByteLiteral(sf.longRegs[i])) { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.NUMBER_CONVERSION_ERROR, "'" + TypeConstants.INT_TNAME + "' value '" + sf.longRegs[i] + "' cannot be converted to '" + TypeConstants.BYTE_TNAME + "'")); handleError(ctx); break; } sf.longRegs[j] = sf.longRegs[i]; break; case InstructionCodes.F2BI: i = operands[0]; j = operands[1]; double floatVal = sf.doubleRegs[i]; if (Double.isNaN(floatVal) || Double.isInfinite(floatVal)) { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.NUMBER_CONVERSION_ERROR, "'" + TypeConstants.FLOAT_TNAME + "' value '" + floatVal + "' cannot be converted to '" + TypeConstants.BYTE_TNAME + "'")); handleError(ctx); break; } long floatAsIntVal = Math.round(floatVal); if (!isByteLiteral(floatAsIntVal)) { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.NUMBER_CONVERSION_ERROR, "'" + TypeConstants.FLOAT_TNAME + "' value '" + floatVal + "' cannot be converted to '" + TypeConstants.BYTE_TNAME + "'")); handleError(ctx); break; } sf.longRegs[j] = floatAsIntVal; break; case InstructionCodes.D2BI: i = operands[0]; j = operands[1]; DecimalValueKind valueKind = ((BDecimal) sf.refRegs[i]).valueKind; if (valueKind == DecimalValueKind.NOT_A_NUMBER || valueKind == DecimalValueKind.NEGATIVE_INFINITY || valueKind == DecimalValueKind.POSITIVE_INFINITY) { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.NUMBER_CONVERSION_ERROR, "'" + TypeConstants.DECIMAL_TNAME + "' value '" + sf.refRegs[i] + "' cannot be converted to '" + TypeConstants.BYTE_TNAME + "'")); handleError(ctx); break; } long doubleAsIntVal = Math.round(((BDecimal) sf.refRegs[i]).floatValue()); if (!isByteLiteral(doubleAsIntVal)) { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.NUMBER_CONVERSION_ERROR, "'" + TypeConstants.DECIMAL_TNAME + "' value '" + sf.refRegs[i] + "' cannot be converted to '" + TypeConstants.BYTE_TNAME + "'")); handleError(ctx); break; } sf.longRegs[j] = doubleAsIntVal; break; case InstructionCodes.F2I: i = operands[0]; j = operands[1]; double valueToConvert = sf.doubleRegs[i]; if (Double.isNaN(valueToConvert) || Double.isInfinite(valueToConvert)) { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.NUMBER_CONVERSION_ERROR, "'" + TypeConstants.FLOAT_TNAME + "' value '" + valueToConvert + "' cannot be converted to '" + TypeConstants.INT_TNAME + "'")); handleError(ctx); break; } if (!isFloatWithinIntRange(valueToConvert)) { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.NUMBER_CONVERSION_ERROR, "out " + "of range '" + TypeConstants.FLOAT_TNAME + "' value '" + valueToConvert + "' cannot be converted to '" + TypeConstants.INT_TNAME + "'")); handleError(ctx); break; } sf.longRegs[j] = Math.round(valueToConvert); break; case InstructionCodes.F2S: i = operands[0]; j = operands[1]; sf.stringRegs[j] = Double.toString(sf.doubleRegs[i]); break; case InstructionCodes.F2B: i = operands[0]; j = operands[1]; sf.intRegs[j] = sf.doubleRegs[i] != 0.0 ? 1 : 0; break; case InstructionCodes.F2D: i = operands[0]; j = operands[1]; sf.refRegs[j] = new BDecimal(new BigDecimal(sf.doubleRegs[i], MathContext.DECIMAL128)); break; case InstructionCodes.S2I: i = operands[0]; j = operands[1]; str = sf.stringRegs[i]; try { sf.refRegs[j] = new BInteger(Long.parseLong(str)); } catch (NumberFormatException e) { handleTypeConversionError(ctx, sf, j, TypeConstants.STRING_TNAME, TypeConstants.INT_TNAME); } break; case InstructionCodes.S2F: i = operands[0]; j = operands[1]; str = sf.stringRegs[i]; try { sf.refRegs[j] = new BFloat(Double.parseDouble(str)); } catch (NumberFormatException e) { handleTypeConversionError(ctx, sf, j, TypeConstants.STRING_TNAME, TypeConstants.FLOAT_TNAME); } break; case InstructionCodes.S2B: i = operands[0]; j = operands[1]; sf.intRegs[j] = Boolean.parseBoolean(sf.stringRegs[i]) ? 1 : 0; break; case InstructionCodes.S2D: i = operands[0]; j = operands[1]; str = sf.stringRegs[i]; try { sf.refRegs[j] = new BDecimal(new BigDecimal(str, MathContext.DECIMAL128)); } catch (NumberFormatException e) { handleTypeConversionError(ctx, sf, j, TypeConstants.STRING_TNAME, TypeConstants.DECIMAL_TNAME); } break; case InstructionCodes.B2I: i = operands[0]; j = operands[1]; sf.longRegs[j] = sf.intRegs[i]; break; case InstructionCodes.B2F: i = operands[0]; j = operands[1]; sf.doubleRegs[j] = sf.intRegs[i]; break; case InstructionCodes.B2S: i = operands[0]; j = operands[1]; sf.stringRegs[j] = sf.intRegs[i] == 1 ? "true" : "false"; break; case InstructionCodes.B2D: i = operands[0]; j = operands[1]; sf.refRegs[j] = sf.intRegs[i] == 1 ? new BDecimal(BigDecimal.ONE.setScale(1, BigDecimal.ROUND_HALF_EVEN)) : new BDecimal(BigDecimal.ZERO.setScale(1, BigDecimal.ROUND_HALF_EVEN)); break; case InstructionCodes.D2I: i = operands[0]; j = operands[1]; BDecimal decimal = (BDecimal) sf.refRegs[i]; DecimalValueKind decValueKind = decimal.valueKind; if (decValueKind == DecimalValueKind.NOT_A_NUMBER || decValueKind == DecimalValueKind.NEGATIVE_INFINITY || decValueKind == DecimalValueKind.POSITIVE_INFINITY) { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.NUMBER_CONVERSION_ERROR, "'" + TypeConstants.DECIMAL_TNAME + "' value '" + sf.refRegs[i] + "' cannot be converted to '" + TypeConstants.INT_TNAME + "'")); handleError(ctx); break; } if (!isDecimalWithinIntRange((decimal.decimalValue()))) { ctx.setError(BLangVMErrors.createError(ctx, BallerinaErrorReasons.NUMBER_CONVERSION_ERROR, "out of range '" + TypeConstants.DECIMAL_TNAME + "' value '" + decimal + "' cannot be converted to '" + TypeConstants.INT_TNAME + "'")); handleError(ctx); break; } sf.longRegs[j] = Math.round(((BDecimal) sf.refRegs[i]).decimalValue().doubleValue()); break; case InstructionCodes.D2F: i = operands[0]; j = operands[1]; sf.doubleRegs[j] = ((BDecimal) sf.refRegs[i]).floatValue(); break; case InstructionCodes.D2S: i = operands[0]; j = operands[1]; sf.stringRegs[j] = sf.refRegs[i].stringValue(); break; case InstructionCodes.D2B: i = operands[0]; j = operands[1]; sf.intRegs[j] = ((BDecimal) sf.refRegs[i]).decimalValue().compareTo(BigDecimal.ZERO) != 0 ? 1 : 0; break; case InstructionCodes.DT2XML: i = operands[0]; j = operands[1]; bRefType = sf.refRegs[i]; if (bRefType == null) { handleTypeConversionError(ctx, sf, j, BTypes.typeNull, BTypes.typeXML); break; } try { sf.refRegs[j] = XMLUtils.tableToXML((BTable) bRefType); } catch (Exception e) { sf.refRegs[j] = null; handleTypeConversionError(ctx, sf, j, TypeConstants.TABLE_TNAME, TypeConstants.XML_TNAME); } break; case InstructionCodes.DT2JSON: i = operands[0]; j = operands[1]; bRefType = sf.refRegs[i]; if (bRefType == null) { handleNullRefError(ctx); break; } try { sf.refRegs[j] = JSONUtils.toJSON((BTable) bRefType); } catch (Exception e) { handleTypeConversionError(ctx, sf, j, TypeConstants.TABLE_TNAME, TypeConstants.XML_TNAME); } break; case InstructionCodes.T2MAP: convertStructToMap(ctx, operands, sf); break; case InstructionCodes.T2JSON: convertStructToJSON(ctx, operands, sf); break; case InstructionCodes.MAP2JSON: convertMapToJSON(ctx, operands, sf); break; case InstructionCodes.JSON2MAP: convertJSONToMap(ctx, operands, sf); break; case InstructionCodes.MAP2T: convertMapToStruct(ctx, operands, sf); break; case InstructionCodes.JSON2T: convertJSONToStruct(ctx, operands, sf); break; case InstructionCodes.XMLATTRS2MAP: i = operands[0]; j = operands[1]; bRefType = sf.refRegs[i]; sf.refRegs[j] = ((BXMLAttributes) sf.refRegs[i]).value(); break; case InstructionCodes.XML2S: i = operands[0]; j = operands[1]; sf.stringRegs[j] = sf.refRegs[i].stringValue(); break; case InstructionCodes.ANY2SCONV: i = operands[0]; j = operands[1]; bRefType = sf.refRegs[i]; if (bRefType == null) { sf.stringRegs[j] = STRING_NULL_VALUE; } else { sf.stringRegs[j] = bRefType.stringValue(); } break; default: throw new UnsupportedOperationException(); } } public static boolean isByteLiteral(long longValue) { return (longValue >= BBYTE_MIN_VALUE && longValue <= BBYTE_MAX_VALUE); } public static boolean isFloatWithinIntRange(double doubleValue) { return doubleValue < BINT_MAX_VALUE_DOUBLE_RANGE_MAX && doubleValue > BINT_MIN_VALUE_DOUBLE_RANGE_MIN; } public static boolean isDecimalWithinIntRange(BigDecimal decimalValue) { return decimalValue.compareTo(BINT_MAX_VALUE_BIG_DECIMAL_RANGE_MAX) < 0 && decimalValue.compareTo(BINT_MIN_VALUE_BIG_DECIMAL_RANGE_MIN) > 0; } private static boolean isDecimalRealNumber(BDecimal decimalValue) { return decimalValue.valueKind == DecimalValueKind.ZERO || decimalValue.valueKind == DecimalValueKind.OTHER; } private static boolean checkDecimalGreaterThan(BDecimal lhsValue, BDecimal rhsValue) { switch (lhsValue.valueKind) { case POSITIVE_INFINITY: return isDecimalRealNumber(rhsValue) || rhsValue.valueKind == DecimalValueKind.NEGATIVE_INFINITY; case ZERO: case OTHER: return rhsValue.valueKind == DecimalValueKind.NEGATIVE_INFINITY || (isDecimalRealNumber(rhsValue) && lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) > 0); default: return false; } } private static boolean checkDecimalGreaterThanOrEqual(BDecimal lhsValue, BDecimal rhsValue) { return checkDecimalGreaterThan(lhsValue, rhsValue) || (isDecimalRealNumber(lhsValue) && isDecimalRealNumber(rhsValue) && lhsValue.decimalValue().compareTo(rhsValue.decimalValue()) == 0); } private static void execIteratorOperation(Strand ctx, StackFrame sf, Instruction instruction) { BIterator iterator; InstructionIteratorNext nextInstruction; nextInstruction = (InstructionIteratorNext) instruction; iterator = (BIterator) sf.refRegs[nextInstruction.iteratorIndex]; try { // Check whether we have a next value. if (!Optional.of(iterator).get().hasNext()) { // If we don't have a next value, that means we have reached the end of the iterable list. So // we set null to the corresponding registry location. sf.refRegs[nextInstruction.retRegs[0]] = null; return; } // Get the next value. BValue value = Optional.of(iterator).get().getNext(); // We create a new map and add the value to the map with the key `value`. Then we set this // map to the corresponding registry location. BMap<String, BValue> newMap = new BMap<>(nextInstruction.constraintType); newMap.put("value", value); sf.refRegs[nextInstruction.retRegs[0]] = (BRefType) newMap; } catch (BallerinaException e) { ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), e.getDetail())); handleError(ctx); } } private static void execXMLCreationOpcodes(Strand ctx, StackFrame sf, int opcode, int[] operands) { int i; int j; int k; int l; BXML<?> xmlVal; switch (opcode) { case InstructionCodes.NEWXMLELEMENT: i = operands[0]; j = operands[1]; k = operands[2]; l = operands[3]; BXMLQName startTagName = (BXMLQName) sf.refRegs[j]; BXMLQName endTagName = (BXMLQName) sf.refRegs[k]; try { sf.refRegs[i] = XMLUtils.createXMLElement(startTagName, endTagName, sf.stringRegs[l]); } catch (Exception e) { ctx.setError( BLangVMErrors.createError(ctx, BallerinaErrorReasons.XML_CREATION_ERROR, e.getMessage())); handleError(ctx); } break; case InstructionCodes.NEWXMLCOMMENT: i = operands[0]; j = operands[1]; sf.refRegs[i] = XMLUtils.createXMLComment(sf.stringRegs[j]); break; case InstructionCodes.NEWXMLTEXT: i = operands[0]; j = operands[1]; sf.refRegs[i] = XMLUtils.createXMLText(sf.stringRegs[j]); break; case InstructionCodes.NEWXMLPI: i = operands[0]; j = operands[1]; k = operands[2]; sf.refRegs[i] = XMLUtils.createXMLProcessingInstruction(sf.stringRegs[j], sf.stringRegs[k]); break; case InstructionCodes.XMLSEQSTORE: i = operands[0]; j = operands[1]; xmlVal = (BXML<?>) sf.refRegs[i]; BXML<?> child = (BXML<?>) sf.refRegs[j]; xmlVal.addChildren(child); break; case InstructionCodes.NEWXMLSEQ: i = operands[0]; sf.refRegs[i] = new BXMLSequence(); break; } } private static boolean handleVariableLock(Strand strand, BType[] types, int[] pkgRegs, int[] varRegs, int[] fieldRegs, int varCount, String uuid) { boolean lockAcquired = true; for (int i = 0; i < varCount && lockAcquired; i++) { BType paramType = types[i]; int pkgIndex = pkgRegs[i]; int regIndex = varRegs[i]; switch (paramType.getTag()) { case TypeTags.INT_TAG: lockAcquired = strand.programFile.globalMemArea.lockIntField(strand, pkgIndex, regIndex); break; case TypeTags.BYTE_TAG: lockAcquired = strand.programFile.globalMemArea.lockBooleanField(strand, pkgIndex, regIndex); break; case TypeTags.FLOAT_TAG: lockAcquired = strand.programFile.globalMemArea.lockFloatField(strand, pkgIndex, regIndex); break; case TypeTags.STRING_TAG: lockAcquired = strand.programFile.globalMemArea.lockStringField(strand, pkgIndex, regIndex); break; case TypeTags.BOOLEAN_TAG: lockAcquired = strand.programFile.globalMemArea.lockBooleanField(strand, pkgIndex, regIndex); break; default: lockAcquired = strand.programFile.globalMemArea.lockRefField(strand, pkgIndex, regIndex); } } if (varRegs.length <= varCount) { return lockAcquired; } //lock on field access strand.currentFrame.localProps.putIfAbsent(uuid, new Stack<VarLock>()); Stack lockStack = (Stack) strand.currentFrame.localProps.get(uuid); for (int i = 0; (i < varRegs.length - varCount) && lockAcquired; i++) { int regIndex = varRegs[varCount + i]; String field = strand.currentFrame.stringRegs[fieldRegs[i]]; VarLock lock = ((BMap) strand.currentFrame.refRegs[regIndex]).getFieldLock(field); lockAcquired = lock.lock(strand); if (lockAcquired) { lockStack.push(lock); } } return lockAcquired; } private static void handleVariableUnlock(Strand strand, BType[] types, int[] pkgRegs, int[] varRegs, int varCount, String uuid, boolean hasFieldVar) { if (hasFieldVar) { Stack<VarLock> lockStack = (Stack<VarLock>) strand.currentFrame.localProps.get(uuid); while (!lockStack.isEmpty()) { lockStack.pop().unlock(); } } for (int i = varCount - 1; i > -1; i--) { BType paramType = types[i]; int pkgIndex = pkgRegs[i]; int regIndex = varRegs[i]; switch (paramType.getTag()) { case TypeTags.INT_TAG: strand.programFile.globalMemArea.unlockIntField(pkgIndex, regIndex); break; case TypeTags.BYTE_TAG: strand.programFile.globalMemArea.unlockBooleanField(pkgIndex, regIndex); break; case TypeTags.FLOAT_TAG: strand.programFile.globalMemArea.unlockFloatField(pkgIndex, regIndex); break; case TypeTags.STRING_TAG: strand.programFile.globalMemArea.unlockStringField(pkgIndex, regIndex); break; case TypeTags.BOOLEAN_TAG: strand.programFile.globalMemArea.unlockBooleanField(pkgIndex, regIndex); break; default: strand.programFile.globalMemArea.unlockRefField(pkgIndex, regIndex); } } } /** * Method to calculate and detect debug points when the instruction point is given. */ private static boolean debug(Strand ctx) { Debugger debugger = ctx.programFile.getDebugger(); if (!debugger.isClientSessionActive()) { return false; } DebugContext debugContext = ctx.getDebugContext(); if (debugContext.isStrandPaused()) { debugContext.setStrandPaused(false); return false; } if (isIgnorableInstruction(ctx)) { return false; } LineNumberInfo currentExecLine = debugger.getLineNumber( ctx.currentFrame.callableUnitInfo.getPackageInfo().getPkgPath(), ctx.currentFrame.ip); /* Below if check stops hitting the same debug line again and again in case that single line has multiple instructions. */ if (currentExecLine.equals(debugContext.getLastLine())) { return false; } if (debugPointCheck(ctx, currentExecLine, debugger)) { return true; } switch (debugContext.getCurrentCommand()) { case RESUME: /* In case of a for loop, need to clear the last hit line, so that, same line can get hit again. */ debugContext.clearContext(); break; case STEP_IN: debugHit(ctx, currentExecLine, debugger); return true; case STEP_OVER: if (debugContext.getFramePointer() < ctx.fp) { return false; } debugHit(ctx, currentExecLine, debugger); return true; case STEP_OUT: if (debugContext.getFramePointer() > ctx.fp) { debugHit(ctx, currentExecLine, debugger); return true; } return false; default: debugger.notifyExit(); debugger.stopDebugging(); } return false; } private static boolean isIgnorableInstruction(Strand ctx) { int opcode = ctx.currentFrame.code[ctx.currentFrame.ip].getOpcode(); return opcode == InstructionCodes.GOTO; } /** * Helper method to check whether given point is a debug point or not. * If it's a debug point, then notify the debugger. * * @param ctx Current ctx. * @param currentExecLine Current execution line. * @param debugger Debugger object. * @return Boolean true if it's a debug point, false otherwise. */ private static boolean debugPointCheck(Strand ctx, LineNumberInfo currentExecLine, Debugger debugger) { if (!currentExecLine.isDebugPoint()) { return false; } debugHit(ctx, currentExecLine, debugger); return true; } /** * Helper method to set required details when a debug point hits. * And also to notify the debugger. * * @param ctx Current ctx. * @param currentExecLine Current execution line. * @param debugger Debugger object. */ private static void debugHit(Strand ctx, LineNumberInfo currentExecLine, Debugger debugger) { ctx.getDebugContext().updateContext(currentExecLine, ctx.fp); debugger.pauseWorker(ctx); debugger.notifyDebugHit(ctx, currentExecLine, ctx.getId()); } private static void handleAnyToRefTypeCast(Strand ctx, StackFrame sf, int[] operands, BType targetType) { int i = operands[0]; int j = operands[1]; BRefType bRefType = sf.refRegs[i]; if (bRefType == null) { sf.refRegs[j] = null; } else if (bRefType.getType() == targetType) { sf.refRegs[j] = bRefType; } else { handleTypeCastError(ctx, sf, j, bRefType.getType(), targetType); } } private static void handleTypeCastError(Strand ctx, StackFrame sf, int errorRegIndex, BType sourceType, BType targetType) { handleTypeCastError(ctx, sf, errorRegIndex, sourceType.toString(), targetType.toString()); } private static void handleTypeCastError(Strand ctx, StackFrame sf, int errorRegIndex, String sourceType, String targetType) { BError errorVal = BLangVMErrors.createTypeCastError(ctx, sourceType, targetType); sf.refRegs[errorRegIndex] = errorVal; } private static void handleTypeConversionError(Strand ctx, StackFrame sf, int errorRegIndex, BType sourceType, BType targetType) { handleTypeConversionError(ctx, sf, errorRegIndex, sourceType.toString(), targetType.toString()); } private static void handleTypeConversionError(Strand ctx, StackFrame sf, int errorRegIndex, String sourceTypeName, String targetTypeName) { String errorMsg = "'" + sourceTypeName + "' cannot be converted to '" + targetTypeName + "'"; handleTypeConversionError(ctx, sf, errorRegIndex, errorMsg); } private static void handleTypeConversionError(Strand ctx, StackFrame sf, int errorRegIndex, String errorMessage) { BError errorVal = BLangVMErrors.createTypeConversionError(ctx, errorMessage); sf.refRegs[errorRegIndex] = errorVal; } private static void createNewIntRange(int[] operands, StackFrame sf) { long startValue = sf.longRegs[operands[0]]; long endValue = sf.longRegs[operands[1]]; sf.refRegs[operands[2]] = new BIntRange(startValue, endValue); } private static void createNewStruct(int[] operands, StackFrame sf) { int cpIndex = operands[0]; int i = operands[1]; StructureRefCPEntry structureRefCPEntry = (StructureRefCPEntry) sf.constPool[cpIndex]; StructureTypeInfo structInfo = (StructureTypeInfo) ((TypeDefInfo) structureRefCPEntry .getStructureTypeInfo()).typeInfo; sf.refRegs[i] = new BMap<>(structInfo.getType()); } private static void beginTransaction(Strand strand, int transactionType, int transactionBlockIdIndex, int retryCountRegIndex, int committedFuncIndex, int abortedFuncIndex) { if (transactionType == Transactions.TransactionType.PARTICIPANT.value) { beginTransactionLocalParticipant(strand, transactionBlockIdIndex, committedFuncIndex, abortedFuncIndex); return; } else if (transactionType == Transactions.TransactionType.REMOTE_PARTICIPANT.value) { beginRemoteParticipant(strand, transactionBlockIdIndex, committedFuncIndex, abortedFuncIndex); return; } //Transaction is attempted three times by default to improve resiliency int retryCount = TransactionConstants.DEFAULT_RETRY_COUNT; if (retryCountRegIndex != -1) { retryCount = (int) strand.currentFrame.longRegs[retryCountRegIndex]; if (retryCount < 0) { strand.setError(BLangVMErrors.createError(strand, BallerinaErrorReasons.TRANSACTION_ERROR, BLangExceptionHelper.getErrorMessage(RuntimeErrors.INVALID_RETRY_COUNT))); handleError(strand); return; } } // If global tx enabled, it is managed via transaction coordinator. // Otherwise it is managed locally without any interaction with the transaction coordinator. if (strand.getLocalTransactionContext() != null) { // starting a transaction within already infected transaction. createAndSetDynamicNestedTrxError(strand); handleError(strand); return; } String transactionBlockId = getTrxBlockIdFromCP(strand, transactionBlockIdIndex); TransactionLocalContext transactionLocalContext = createAndNotifyGlobalTx(strand, transactionBlockId); strand.setLocalTransactionContext(transactionLocalContext); transactionLocalContext.beginTransactionBlock(transactionBlockId, retryCount); } private static void beginRemoteParticipant(Strand strand, int transactionBlockIdIndex, int committedFuncIndex, int abortedFuncIndex) { TransactionLocalContext localTransactionContext = strand.getLocalTransactionContext(); if (localTransactionContext == null) { // No transaction available to participate, // We have no business here. This is a no-op. return; } // Register committed function handler if exists. BFunctionPointer fpCommitted = null; if (committedFuncIndex != -1) { FunctionRefCPEntry funcRefCPEntry = (FunctionRefCPEntry) strand.currentFrame.constPool[committedFuncIndex]; fpCommitted = new BFunctionPointer(funcRefCPEntry.getFunctionInfo()); } // Register aborted function handler if exists. BFunctionPointer fpAborted = null; if (abortedFuncIndex != -1) { FunctionRefCPEntry funcRefCPEntry = (FunctionRefCPEntry) strand.currentFrame.constPool[abortedFuncIndex]; fpAborted = new BFunctionPointer(funcRefCPEntry.getFunctionInfo()); } String transactionBlockId = getTrxBlockIdFromCP(strand, transactionBlockIdIndex); localTransactionContext.setResourceParticipant(true); String globalTransactionId = localTransactionContext.getGlobalTransactionId(); localTransactionContext.beginTransactionBlock(transactionBlockId, -1); TransactionResourceManager.getInstance().registerParticipation(globalTransactionId, transactionBlockId, fpCommitted, fpAborted, strand); strand.currentFrame.trxParticipant = StackFrame.TransactionParticipantType.REMOTE_PARTICIPANT; } private static void createAndSetDynamicNestedTrxError(Strand strand) { BError error = BLangVMErrors.createError(strand, BallerinaErrorReasons.TRANSACTION_ERROR, BLangExceptionHelper.getErrorMessage(RuntimeErrors.INVALID_DYNAMICALLY_NESTED_TRANSACTION)); strand.setError(error); } private static void beginTransactionLocalParticipant(Strand strand, int transactionBlockIdCpIndex, int committedFuncIndex, int abortedFuncIndex) { TransactionLocalContext transactionLocalContext = strand.getLocalTransactionContext(); if (transactionLocalContext == null) { // No transaction available to participate, // We have no business here. This is a no-op. return; } String transactionBlockId = getTrxBlockIdFromCP(strand, transactionBlockIdCpIndex); // Register committed function handler if exists. TransactionResourceManager transactionResourceManager = TransactionResourceManager.getInstance(); BFunctionPointer fpCommitted = null; if (committedFuncIndex != -1) { FunctionRefCPEntry funcRefCPEntry = (FunctionRefCPEntry) strand.currentFrame.constPool[committedFuncIndex]; fpCommitted = new BFunctionPointer(funcRefCPEntry.getFunctionInfo()); transactionResourceManager.registerCommittedFunction(transactionBlockId, fpCommitted); } // Register aborted function handler if exists. BFunctionPointer fpAborted = null; if (abortedFuncIndex != -1) { FunctionRefCPEntry funcRefCPEntry = (FunctionRefCPEntry) strand.currentFrame.constPool[abortedFuncIndex]; fpAborted = new BFunctionPointer(funcRefCPEntry.getFunctionInfo()); transactionResourceManager.registerAbortedFunction(transactionBlockId, fpAborted); } transactionLocalContext.beginTransactionBlock(transactionBlockId, 1); transactionResourceManager.registerParticipation(transactionLocalContext.getGlobalTransactionId(), transactionBlockId, fpCommitted, fpAborted, strand); // this call frame is a transaction participant. strand.currentFrame.trxParticipant = StackFrame.TransactionParticipantType.LOCAL_PARTICIPANT; } private static String getTrxBlockIdFromCP(Strand strand, int index) { StringCPEntry stringCPEntry = (StringCPEntry) strand.currentFrame.constPool[index]; return stringCPEntry.getValue(); } private static TransactionLocalContext createAndNotifyGlobalTx(Strand ctx, String transactionBlockId) { BValue[] txResult = TransactionUtils.notifyTransactionBegin(ctx, null, null, transactionBlockId, TransactionConstants.DEFAULT_COORDINATION_TYPE); BMap<String, BValue> txDataStruct = (BMap<String, BValue>) txResult[0]; String globalTransactionId = txDataStruct.get(TransactionConstants.TRANSACTION_ID).stringValue(); String url = txDataStruct.get(TransactionConstants.REGISTER_AT_URL).stringValue(); String protocol = txDataStruct.get(TransactionConstants.CORDINATION_TYPE).stringValue(); return TransactionLocalContext.create(globalTransactionId, url, protocol); } private static TransactionLocalContext createLocalOnlyTransaction() { String globalTransactionId = UUID.randomUUID().toString().replaceAll("-", ""); return TransactionLocalContext.create(globalTransactionId, null, null); } private static void retryTransaction(Strand strand, int transactionBlockIdCpIndex, int trAbortEndIp, int trEndStatusReg) { strand.currentFrame.intRegs[trEndStatusReg] = 0; // set trend status to normal. TransactionLocalContext transactionLocalContext = strand.getLocalTransactionContext(); transactionLocalContext.getAndClearFailure(); String transactionBlockId = getTrxBlockIdFromCP(strand, transactionBlockIdCpIndex); if (transactionLocalContext.isRetryPossible(strand, transactionBlockId)) { if (transactionLocalContext.isRetryAttempt(transactionBlockId)) { TransactionLocalContext newLocalTransaction = createAndNotifyGlobalTx(strand, transactionBlockId); int allowedRetryCount = transactionLocalContext.getAllowedRetryCount(transactionBlockId); newLocalTransaction.beginTransactionBlock(transactionBlockId, allowedRetryCount - 1); strand.setLocalTransactionContext(newLocalTransaction); } strand.getLocalTransactionContext().incrementCurrentRetryCount(transactionBlockId); strand.setError(null); // todo: communicate re-try intent to coordinator // tr_end will communicate the tx ending to coordinator return; } strand.currentFrame.intRegs[trEndStatusReg] = 1; strand.currentFrame.ip = trAbortEndIp; } private static void endTransaction(Strand strand, int transactionBlockIdCpIndex, int endType, int statusRegIndex, int errorRegIndex) { TransactionLocalContext localTxInfo = strand.getLocalTransactionContext(); String txBlockId = getTrxBlockIdFromCP(strand, transactionBlockIdCpIndex); try { //In success case no need to do anything as with the transaction end phase it will be committed. switch (Transactions.TransactionStatus.getConst(endType)) { case BLOCK_END: // 0 // set statusReg transactionBlockEnd(strand, txBlockId, statusRegIndex, localTxInfo, errorRegIndex); break; case FAILED: // -1 transactionFailedEnd(strand, txBlockId, localTxInfo, statusRegIndex); break; case ABORTED: // -2 transactionAbortedEnd(strand, txBlockId, localTxInfo, statusRegIndex, errorRegIndex); break; case END: // 1 transactionEndEnd(strand, txBlockId, localTxInfo); break; default: throw new IllegalArgumentException("Invalid transaction end endType: " + endType); } } catch (Throwable e) { strand.setError( BLangVMErrors.createError(strand, BallerinaErrorReasons.TRANSACTION_ERROR, e.getMessage())); handleError(strand); } } private static void transactionAbortedEnd(Strand strand, String txBlockId, TransactionLocalContext localTxInfo, int statusRegIndex, int errorRegIndex) { // Notify only if, aborted by 'abort' statement. if (strand.currentFrame.intRegs[statusRegIndex] == 0) { notifyTransactionAbort(strand, txBlockId, localTxInfo); } setErrorRethrowReg(strand, statusRegIndex, errorRegIndex); } private static void setErrorRethrowReg(Strand strand, int statusRegIndex, int errorRegIndex) { if (strand.getError() != null) { BError cause = strand.getError(); if (cause != null) { strand.setError(cause); strand.currentFrame.refRegs[errorRegIndex] = cause; // panic on this error. } // Next 2 instructions re-throw this error. strand.currentFrame.intRegs[statusRegIndex] = 1; return; } // Skip rethrow instruction, since there is no error. strand.currentFrame.intRegs[statusRegIndex] = 0; } private static void transactionEndEnd(Strand strand, String transactionBlockId, TransactionLocalContext transactionLocalContext) { boolean isOuterTx = transactionLocalContext.onTransactionEnd(transactionBlockId); strand.removeLocalTransactionContext(); } private static void transactionBlockEnd(Strand strand, String transactionBlockId, int statusRegIndex, TransactionLocalContext transactionLocalContext, int errorRegIndex) { // Tx reached end of block, it may or may not successfully finished. TransactionLocalContext.TransactionFailure failure = transactionLocalContext.getFailure(); BError error = null; BRefType<?> errorVal = strand.currentFrame.refRegs[errorRegIndex]; if (errorVal != null && errorVal.getType().getTag() == TypeTags.ERROR_TAG) { error = (BError) errorVal; } if (error != null && strand.getError() == null) { strand.setError(error); strand.currentFrame.refRegs[errorRegIndex] = null; } if (failure == null && error == null && strand.getError() == null) { // Skip branching to retry block as there is no local failure. // Will set this reg if there is failure in global coordinated trx. strand.currentFrame.intRegs[statusRegIndex] = 0; TransactionUtils.CoordinatorCommit coordinatorStatus = notifyGlobalPrepareAndCommit(strand, transactionBlockId, transactionLocalContext); if (!TransactionUtils.CoordinatorCommit.COMMITTED.equals(coordinatorStatus)) { // Coordinator returned un-committed status, hence skip committed block, and goto failed block. strand.currentFrame.intRegs[statusRegIndex] = 1; } } else { // Tx failed, branch to retry block. strand.currentFrame.intRegs[statusRegIndex] = 1; // This could be a transaction failure from a native/std library. Or due to an an error/exception. // If transaction is not already marked as failed do so now for future references. if (failure == null) { transactionLocalContext.markFailure(); } boolean notifyCoordinator = transactionLocalContext.onTransactionFailed(strand, transactionBlockId); if (notifyCoordinator) { notifyTransactionAbort(strand, transactionBlockId, transactionLocalContext); } } } private static TransactionUtils.CoordinatorCommit notifyGlobalPrepareAndCommit(Strand ctx, String transactionBlockId, TransactionLocalContext transactionLocalContext) { return TransactionUtils.notifyTransactionEnd(ctx, transactionLocalContext.getGlobalTransactionId(), transactionBlockId); } private static void notifyTransactionAbort(Strand strand, String transactionBlockId, TransactionLocalContext transactionLocalContext) { TransactionUtils.notifyTransactionAbort(strand, transactionLocalContext.getGlobalTransactionId(), transactionBlockId); TransactionResourceManager.getInstance().notifyAbort(transactionLocalContext.getGlobalTransactionId(), transactionBlockId, false); } private static void transactionFailedEnd(Strand strand, String transactionBlockId, TransactionLocalContext transactionLocalContext, int runOnRetryBlockRegIndex) { // Invoking tr_end with transaction status of FAILED means tx has failed for some reason. if (transactionLocalContext.isRetryPossible(strand, transactionBlockId)) { strand.currentFrame.intRegs[runOnRetryBlockRegIndex] = 1; } else { strand.currentFrame.intRegs[runOnRetryBlockRegIndex] = 0; } } private static Strand invokeVirtualFunction(Strand ctx, StackFrame sf, int receiver, FunctionInfo virtualFuncInfo, int[] argRegs, int retReg, int flags) { BMap<String, BValue> structVal = (BMap<String, BValue>) sf.refRegs[receiver]; // TODO use ObjectTypeInfo once record init function is removed StructureTypeInfo structInfo = (StructureTypeInfo) ((BStructureType) structVal.getType()).getTypeInfo(); FunctionInfo attachedFuncInfo = structInfo.funcInfoEntries.get(virtualFuncInfo.getName()); return invokeCallable(ctx, attachedFuncInfo, argRegs, retReg, flags); } private static void handleWorkerSend(Strand ctx, WorkerDataChannelInfo workerDataChannelInfo, BType type, int reg, boolean channelInSameStrand) { BRefType val = extractValue(ctx.currentFrame, type, reg); WorkerDataChannel dataChannel = getWorkerChannel(ctx, workerDataChannelInfo.getChannelName(), channelInSameStrand); dataChannel.sendData(val); } private static WorkerDataChannel getWorkerChannel(Strand ctx, String name, boolean channelInSameStrand) { if (channelInSameStrand) { return ctx.currentFrame.wdChannels.getWorkerDataChannel(name); } if (ctx.fp > 0) { return ctx.peekFrame(1).wdChannels.getWorkerDataChannel(name); } return ctx.respCallback.parentChannels.getWorkerDataChannel(name); } private static BRefType extractValue(StackFrame data, BType type, int reg) { BRefType result; switch (type.getTag()) { case TypeTags.INT_TAG: result = new BInteger(data.longRegs[reg]); break; case TypeTags.BYTE_TAG: result = new BByte(data.longRegs[reg]); break; case TypeTags.FLOAT_TAG: result = new BFloat(data.doubleRegs[reg]); break; case TypeTags.STRING_TAG: result = new BString(data.stringRegs[reg]); break; case TypeTags.BOOLEAN_TAG: result = new BBoolean(data.intRegs[reg] > 0); break; default: result = data.refRegs[reg]; } return result; } private static boolean handleWorkerReceive(Strand ctx, WorkerDataChannelInfo workerDataChannelInfo, BType type, int reg, boolean channelInSameStrand) { return getWorkerChannel(ctx, workerDataChannelInfo.getChannelName(), channelInSameStrand).tryTakeData(ctx, type, reg); } public static void copyArgValueForWorkerReceive(StackFrame currentSF, int regIndex, BType paramType, BRefType passedInValue) { switch (paramType.getTag()) { case TypeTags.INT_TAG: currentSF.longRegs[regIndex] = ((BInteger) passedInValue).intValue(); break; case TypeTags.BYTE_TAG: currentSF.longRegs[regIndex] = ((BByte) passedInValue).byteValue(); break; case TypeTags.FLOAT_TAG: currentSF.doubleRegs[regIndex] = ((BFloat) passedInValue).floatValue(); break; case TypeTags.STRING_TAG: currentSF.stringRegs[regIndex] = (passedInValue).stringValue(); break; case TypeTags.BOOLEAN_TAG: currentSF.intRegs[regIndex] = (((BBoolean) passedInValue).booleanValue()) ? 1 : 0; break; default: currentSF.refRegs[regIndex] = passedInValue; } } private static boolean checkFiniteTypeAssignable(BValue bRefTypeValue, BType lhsType) { BFiniteType fType = (BFiniteType) lhsType; if (bRefTypeValue == null) { // we should not reach here return false; } else { Iterator<BValue> valueSpaceItr = fType.valueSpace.iterator(); while (valueSpaceItr.hasNext()) { BValue valueSpaceItem = valueSpaceItr.next(); if (valueSpaceItem.getType().getTag() == bRefTypeValue.getType().getTag()) { if (valueSpaceItem.equals(bRefTypeValue)) { return true; } } } } return false; } private static boolean checkUnionCast(BValue rhsValue, BType lhsType, List<TypePair> unresolvedTypes) { BUnionType unionType = (BUnionType) lhsType; for (BType memberType : unionType.getMemberTypes()) { if (checkCast(rhsValue, memberType, unresolvedTypes)) { return true; } } return false; } public static boolean checkCast(BValue rhsValue, BType lhsType) { return checkCast(rhsValue, lhsType, new ArrayList<>()); } private static boolean checkCast(BValue rhsValue, BType lhsType, List<TypePair> unresolvedTypes) { BType rhsType = rhsValue == null ? BTypes.typeNull : rhsValue.getType(); if (isSameOrAnyType(rhsType, lhsType)) { return true; } if (rhsType.getTag() == TypeTags.INT_TAG && lhsType.getTag() == TypeTags.BYTE_TAG) { return isByteLiteral(((BInteger) rhsValue).intValue()); } if (lhsType.getTag() == TypeTags.UNION_TAG) { return checkUnionCast(rhsValue, lhsType, unresolvedTypes); } if (getElementType(rhsType).getTag() == TypeTags.JSON_TAG) { return checkJSONCast(rhsValue, rhsType, lhsType); } if (lhsType.getTag() == TypeTags.ARRAY_TAG && rhsValue instanceof BNewArray) { BType sourceType = rhsValue.getType(); if (sourceType.getTag() == TypeTags.ARRAY_TAG) { if (((BArrayType) sourceType).getState() == BArrayState.CLOSED_SEALED && ((BArrayType) lhsType).getState() == BArrayState.CLOSED_SEALED && ((BArrayType) sourceType).getSize() != ((BArrayType) lhsType).getSize()) { return false; } sourceType = ((BArrayType) rhsValue.getType()).getElementType(); } return checkArrayCast(sourceType, ((BArrayType) lhsType).getElementType(), unresolvedTypes); } if (rhsType.getTag() == TypeTags.TUPLE_TAG && lhsType.getTag() == TypeTags.TUPLE_TAG) { return checkTupleCast(rhsValue, lhsType, unresolvedTypes); } if (lhsType.getTag() == TypeTags.FINITE_TYPE_TAG) { return checkFiniteTypeAssignable(rhsValue, lhsType); } return checkCastByType(rhsType, lhsType, unresolvedTypes); } /** * This method is for use as the first check in checking for cast/assignability for two types. * Checks whether the source type is the same as the target type or if the target type is any type, and if true * the return value would be true. * * @param rhsType the source type - the type (of the value) being cast/assigned * @param lhsType the target type against which cast/assignability is checked * @return true if the lhsType is any or is the same as rhsType */ private static boolean isSameOrAnyType(BType rhsType, BType lhsType) { return (lhsType.getTag() == TypeTags.ANY_TAG && rhsType.getTag() != TypeTags.ERROR_TAG) || rhsType.equals(lhsType); } private static boolean checkCastByType(BType rhsType, BType lhsType, List<TypePair> unresolvedTypes) { if (rhsType.getTag() == TypeTags.INT_TAG && (lhsType.getTag() == TypeTags.FLOAT_TAG || lhsType.getTag() == TypeTags.DECIMAL_TAG)) { return true; } else if (rhsType.getTag() == TypeTags.FLOAT_TAG && lhsType.getTag() == TypeTags.DECIMAL_TAG) { return true; } else if (rhsType.getTag() == TypeTags.BYTE_TAG && lhsType.getTag() == TypeTags.INT_TAG) { return true; } if (lhsType.getTag() == TypeTags.JSON_TAG) { switch (rhsType.getTag()) { case TypeTags.INT_TAG: case TypeTags.FLOAT_TAG: case TypeTags.DECIMAL_TAG: case TypeTags.STRING_TAG: case TypeTags.BOOLEAN_TAG: case TypeTags.NULL_TAG: case TypeTags.JSON_TAG: return true; case TypeTags.MAP_TAG: return checkCastByType(((BMapType) rhsType).getConstrainedType(), lhsType, unresolvedTypes); case TypeTags.ARRAY_TAG: return checkCastByType(((BArrayType) rhsType).getElementType(), lhsType, unresolvedTypes); default: return false; } } if (rhsType.getTag() == TypeTags.OBJECT_TYPE_TAG && lhsType.getTag() == TypeTags.OBJECT_TYPE_TAG) { return checkObjectEquivalency((BStructureType) rhsType, (BStructureType) lhsType, unresolvedTypes); } if (rhsType.getTag() == TypeTags.RECORD_TYPE_TAG && lhsType.getTag() == TypeTags.RECORD_TYPE_TAG) { return checkRecordEquivalency((BRecordType) lhsType, (BRecordType) rhsType, unresolvedTypes); } if (rhsType.getTag() == TypeTags.MAP_TAG && lhsType.getTag() == TypeTags.MAP_TAG) { return checkMapCast(rhsType, lhsType, unresolvedTypes); } if (rhsType.getTag() == TypeTags.TABLE_TAG && lhsType.getTag() == TypeTags.TABLE_TAG) { return true; } if (rhsType.getTag() == TypeTags.STREAM_TAG && lhsType.getTag() == TypeTags.STREAM_TAG) { return isAssignable(((BStreamType) rhsType).getConstrainedType(), ((BStreamType) lhsType).getConstrainedType(), unresolvedTypes); } if (rhsType.getTag() == TypeTags.FUNCTION_POINTER_TAG && lhsType.getTag() == TypeTags.FUNCTION_POINTER_TAG) { return checkFunctionCast(rhsType, (BFunctionType) lhsType); } if (lhsType.getTag() == TypeTags.ANYDATA_TAG && isAnydata(rhsType)) { return true; } return false; } private static boolean checkMapCast(BType sourceType, BType targetType, List<TypePair> unresolvedTypes) { BMapType sourceMapType = (BMapType) sourceType; BMapType targetMapType = (BMapType) targetType; if (sourceMapType.equals(targetMapType)) { return true; } if (targetMapType.getConstrainedType().getTag() == TypeTags.ANY_TAG) { return true; } if (sourceMapType.getConstrainedType().getTag() == TypeTags.OBJECT_TYPE_TAG && targetMapType.getConstrainedType().getTag() == TypeTags.OBJECT_TYPE_TAG) { return checkObjectEquivalency((BStructureType) sourceMapType.getConstrainedType(), (BStructureType) targetMapType.getConstrainedType(), unresolvedTypes); } if (sourceMapType.getConstrainedType().getTag() == TypeTags.RECORD_TYPE_TAG && targetMapType.getConstrainedType().getTag() == TypeTags.RECORD_TYPE_TAG) { return checkRecordEquivalency((BRecordType) targetMapType.getConstrainedType(), (BRecordType) sourceMapType.getConstrainedType(), unresolvedTypes); } return false; } private static boolean checkArrayCast(BType sourceType, BType targetType, List<TypePair> unresolvedTypes) { if (targetType.getTag() == TypeTags.ARRAY_TAG && sourceType.getTag() == TypeTags.ARRAY_TAG) { BArrayType sourceArrayType = (BArrayType) sourceType; BArrayType targetArrayType = (BArrayType) targetType; if (targetArrayType.getDimensions() > sourceArrayType.getDimensions()) { return false; } return checkArrayCast(sourceArrayType.getElementType(), targetArrayType.getElementType(), unresolvedTypes); } else if (targetType.getTag() == TypeTags.UNION_TAG) { return checkUnionAssignable(sourceType, targetType, unresolvedTypes); } if (targetType.getTag() == TypeTags.ANY_TAG) { return true; } return sourceType.equals(targetType); } private static boolean checkTupleCast(BValue sourceValue, BType targetType, List<TypePair> unresolvedTypes) { BValueArray source = (BValueArray) sourceValue; BTupleType target = (BTupleType) targetType; List<BType> targetTupleTypes = target.getTupleTypes(); if (source.size() != targetTupleTypes.size()) { return false; } for (int i = 0; i < source.size(); i++) { if (!checkCast(source.getBValue(i), targetTupleTypes.get(i), unresolvedTypes)) { return false; } } return true; } private static boolean isAnydata(BType type) { return isAnydata(type, new HashSet<>()); } private static boolean isAnydata(BType type, Set<BType> unresolvedTypes) { if (type.getTag() <= TypeTags.ANYDATA_TAG) { return true; } switch (type.getTag()) { case TypeTags.MAP_TAG: return isAnydata(((BMapType) type).getConstrainedType(), unresolvedTypes); case TypeTags.RECORD_TYPE_TAG: if (unresolvedTypes.contains(type)) { return true; } unresolvedTypes.add(type); BRecordType recordType = (BRecordType) type; List<BType> fieldTypes = recordType.getFields().values().stream().map(BField::getFieldType) .collect(Collectors.toList()); return isAnydata(fieldTypes, unresolvedTypes) && (recordType.sealed || isAnydata(recordType.restFieldType, unresolvedTypes)); case TypeTags.UNION_TAG: return isAnydata(((BUnionType) type).getMemberTypes(), unresolvedTypes); case TypeTags.TUPLE_TAG: return isAnydata(((BTupleType) type).getTupleTypes(), unresolvedTypes); case TypeTags.ARRAY_TAG: return isAnydata(((BArrayType) type).getElementType(), unresolvedTypes); case TypeTags.FINITE_TYPE_TAG: Set<BType> valSpaceTypes = ((BFiniteType) type).valueSpace.stream().map(BValue::getType) .collect(Collectors.toSet()); return isAnydata(valSpaceTypes, unresolvedTypes); default: return false; } } private static boolean isAnydata(Collection<BType> types, Set<BType> unresolvedTypes) { return types.stream().allMatch(bType -> isAnydata(bType, unresolvedTypes)); } private static BType getElementType(BType type) { if (type.getTag() != TypeTags.ARRAY_TAG) { return type; } return getElementType(((BArrayType) type).getElementType()); } public static boolean checkStructEquivalency(BStructureType rhsType, BStructureType lhsType) { return checkObjectEquivalency(rhsType, lhsType, new ArrayList<>()); } private static boolean checkObjectEquivalency(BStructureType rhsType, BStructureType lhsType, List<TypePair> unresolvedTypes) { // If we encounter two types that we are still resolving, then skip it. // This is done to avoid recursive checking of the same type. TypePair pair = new TypePair(rhsType, lhsType); if (unresolvedTypes.contains(pair)) { return true; } unresolvedTypes.add(pair); // Both structs should be public or private. // Get the XOR of both flags(masks) // If both are public, then public bit should be 0; // If both are private, then public bit should be 0; // The public bit is on means, one is public, and the other one is private. if (Flags.isFlagOn(lhsType.flags ^ rhsType.flags, Flags.PUBLIC)) { return false; } // If both structs are private, they should be in the same package. if (!Flags.isFlagOn(lhsType.flags, Flags.PUBLIC) && !rhsType.getPackagePath().equals(lhsType.getPackagePath())) { return false; } // Adjust the number of the attached functions of the lhs struct based on // the availability of the initializer function. int lhsAttachedFunctionCount = lhsType.initializer != null ? lhsType.getAttachedFunctions().length - 1 : lhsType.getAttachedFunctions().length; if (lhsType.getFields().size() > rhsType.getFields().size() || lhsAttachedFunctionCount > rhsType.getAttachedFunctions().length) { return false; } return !Flags.isFlagOn(lhsType.flags, Flags.PUBLIC) && rhsType.getPackagePath().equals(lhsType.getPackagePath()) ? checkPrivateObjectsEquivalency(lhsType, rhsType, unresolvedTypes) : checkPublicObjectsEquivalency(lhsType, rhsType, unresolvedTypes); } public static boolean checkRecordEquivalency(BRecordType lhsType, BRecordType rhsType, List<TypePair> unresolvedTypes) { // Both records should be public or private. // Get the XOR of both flags(masks) // If both are public, then public bit should be 0; // If both are private, then public bit should be 0; // The public bit is on means, one is public, and the other one is private. if (Flags.isFlagOn(lhsType.flags ^ rhsType.flags, Flags.PUBLIC)) { return false; } // If both records are private, they should be in the same package. if (!Flags.isFlagOn(lhsType.flags, Flags.PUBLIC) && !rhsType.getPackagePath().equals(lhsType.getPackagePath())) { return false; } // Cannot assign open records to closed record types if (lhsType.sealed && !rhsType.sealed) { return false; } // The rest field types should match if they are open records if (!rhsType.sealed && !isAssignable(rhsType.restFieldType, lhsType.restFieldType, unresolvedTypes)) { return false; } return checkFieldEquivalency(lhsType, rhsType, unresolvedTypes); } private static boolean checkPrivateObjectsEquivalency(BStructureType lhsType, BStructureType rhsType, List<TypePair> unresolvedTypes) { Map<String, BField> rhsFields = rhsType.getFields(); for (Map.Entry<String, BField> lhsFieldEntry : lhsType.getFields().entrySet()) { BField rhsField = rhsFields.get(lhsFieldEntry.getKey()); if (rhsField == null || !isSameType(rhsField.fieldType, lhsFieldEntry.getValue().fieldType)) { return false; } } BAttachedFunction[] lhsFuncs = lhsType.getAttachedFunctions(); BAttachedFunction[] rhsFuncs = rhsType.getAttachedFunctions(); for (BAttachedFunction lhsFunc : lhsFuncs) { if (lhsFunc == lhsType.initializer || lhsFunc == lhsType.defaultsValuesInitFunc) { continue; } BAttachedFunction rhsFunc = getMatchingInvokableType(rhsFuncs, lhsFunc, unresolvedTypes); if (rhsFunc == null) { return false; } } return true; } private static boolean checkPublicObjectsEquivalency(BStructureType lhsType, BStructureType rhsType, List<TypePair> unresolvedTypes) { // Check the whether there is any private fields in RHS type if (rhsType.getFields().values().stream().anyMatch(field -> !Flags.isFlagOn(field.flags, Flags.PUBLIC))) { return false; } Map<String, BField> rhsFields = rhsType.getFields(); for (Map.Entry<String, BField> lhsFieldEntry : lhsType.getFields().entrySet()) { BField rhsField = rhsFields.get(lhsFieldEntry.getKey()); if (rhsField == null || !Flags.isFlagOn(lhsFieldEntry.getValue().flags, Flags.PUBLIC) || !isSameType(rhsField.fieldType, lhsFieldEntry.getValue().fieldType)) { return false; } } BAttachedFunction[] lhsFuncs = lhsType.getAttachedFunctions(); BAttachedFunction[] rhsFuncs = rhsType.getAttachedFunctions(); for (BAttachedFunction lhsFunc : lhsFuncs) { if (lhsFunc == lhsType.initializer || lhsFunc == lhsType.defaultsValuesInitFunc) { continue; } if (!Flags.isFlagOn(lhsFunc.flags, Flags.PUBLIC)) { return false; } BAttachedFunction rhsFunc = getMatchingInvokableType(rhsFuncs, lhsFunc, unresolvedTypes); if (rhsFunc == null || !Flags.isFlagOn(rhsFunc.flags, Flags.PUBLIC)) { return false; } } // Check for private attached function in RHS type for (BAttachedFunction rhsFunc : rhsFuncs) { if (!Flags.isFlagOn(rhsFunc.flags, Flags.PUBLIC)) { return false; } } return true; } private static boolean checkFieldEquivalency(BRecordType lhsType, BRecordType rhsType, List<TypePair> unresolvedTypes) { Map<String, BField> rhsFields = rhsType.getFields(); Set<String> lhsFieldNames = lhsType.getFields().keySet(); for (BField lhsField : lhsType.getFields().values()) { BField rhsField = rhsFields.get(lhsField.fieldName); // If the LHS field is a required one, there has to be a corresponding required field in the RHS record. if (!Flags.isFlagOn(lhsField.flags, Flags.OPTIONAL) && (rhsField == null || Flags.isFlagOn(rhsField.flags, Flags.OPTIONAL))) { return false; } if (rhsField == null || !isAssignable(rhsField.fieldType, lhsField.fieldType, unresolvedTypes)) { return false; } } if (lhsType.sealed) { return lhsFieldNames.containsAll(rhsFields.keySet()); } return rhsFields.values().stream().filter(field -> !lhsFieldNames.contains(field.fieldName)) .allMatch(field -> isAssignable(field.fieldType, lhsType.restFieldType, unresolvedTypes)); } private static boolean checkFunctionTypeEqualityForObjectType(BFunctionType source, BFunctionType target, List<TypePair> unresolvedTypes) { if (source.paramTypes.length != target.paramTypes.length || source.retParamTypes.length != target.retParamTypes.length) { return false; } for (int i = 0; i < source.paramTypes.length; i++) { if (!isAssignable(source.paramTypes[i], target.paramTypes[i], unresolvedTypes)) { return false; } } if (source.retParamTypes == null && target.retParamTypes == null) { return true; } else if (source.retParamTypes == null || target.retParamTypes == null) { return false; } for (int i = 0; i < source.retParamTypes.length; i++) { if (!isAssignable(source.retParamTypes[i], target.retParamTypes[i], unresolvedTypes)) { return false; } } return true; } private static BAttachedFunction getMatchingInvokableType(BAttachedFunction[] rhsFuncs, BAttachedFunction lhsFunc, List<TypePair> unresolvedTypes) { return Arrays.stream(rhsFuncs).filter(rhsFunc -> lhsFunc.funcName.equals(rhsFunc.funcName)).filter( rhsFunc -> checkFunctionTypeEqualityForObjectType(rhsFunc.type, lhsFunc.type, unresolvedTypes)) .findFirst().orElse(null); } private static boolean isSameType(BType rhsType, BType lhsType) { // First check whether both references points to the same object. if (rhsType == lhsType || rhsType.equals(lhsType)) { return true; } if (rhsType.getTag() == lhsType.getTag() && rhsType.getTag() == TypeTags.ARRAY_TAG) { return checkArrayEquivalent(rhsType, lhsType); } // TODO Support function types, json/map constrained types etc. if (rhsType.getTag() == TypeTags.MAP_TAG && lhsType.getTag() == TypeTags.MAP_TAG) { return lhsType.equals(rhsType); } return false; } private static boolean checkArrayEquivalent(BType actualType, BType expType) { if (expType.getTag() == TypeTags.ARRAY_TAG && actualType.getTag() == TypeTags.ARRAY_TAG) { // Both types are array types BArrayType lhrArrayType = (BArrayType) expType; BArrayType rhsArrayType = (BArrayType) actualType; return checkArrayEquivalent(lhrArrayType.getElementType(), rhsArrayType.getElementType()); } // Now one or both types are not array types and they have to be equal if (expType == actualType) { return true; } return false; } /** * Check the compatibility of casting a JSON to a target type. * * @param json JSON to cast * @param sourceType Type of the source JSON * @param targetType Target type * @return Runtime compatibility for casting */ private static boolean checkJSONCast(BValue json, BType sourceType, BType targetType) { switch (targetType.getTag()) { case TypeTags.STRING_TAG: case TypeTags.INT_TAG: case TypeTags.FLOAT_TAG: case TypeTags.BOOLEAN_TAG: return json != null && json.getType().getTag() == targetType.getTag(); case TypeTags.DECIMAL_TAG: return json != null && json.getType().getTag() == TypeTags.FLOAT_TAG; case TypeTags.ARRAY_TAG: if (json == null || json.getType().getTag() != TypeTags.ARRAY_TAG) { return false; } BArrayType arrayType = (BArrayType) targetType; BValueArray array = (BValueArray) json; for (int i = 0; i < array.size(); i++) { // get the element type of source and json, and recursively check for json casting. BType sourceElementType = sourceType.getTag() == TypeTags.ARRAY_TAG ? ((BArrayType) sourceType).getElementType() : sourceType; if (!checkJSONCast(array.getRefValue(i), sourceElementType, arrayType.getElementType())) { return false; } } return true; case TypeTags.JSON_TAG: return getElementType(sourceType).getTag() == TypeTags.JSON_TAG; case TypeTags.ANY_TAG: case TypeTags.ANYDATA_TAG: return true; default: return false; } } private static void convertStructToMap(Strand strand, int[] operands, StackFrame sf) { int i = operands[0]; int j = operands[1]; // TODO: do validation for type? BMap newMap = new BMap(BTypes.typeMap); ((BMap) sf.refRegs[i]).getMap().forEach( (key, value) -> newMap.put(key, value == null ? null : ((BValue) value).copy(new HashMap<>()))); sf.refRegs[j] = newMap; } private static void convertStructToJSON(Strand ctx, int[] operands, StackFrame sf) { int i = operands[0]; int cpIndex = operands[1]; int j = operands[2]; BJSONType targetType = (BJSONType) ((TypeRefCPEntry) sf.constPool[cpIndex]).getType(); BMap<String, BValue> bStruct = (BMap<String, BValue>) sf.refRegs[i]; try { sf.refRegs[j] = JSONUtils.convertMapToJSON(bStruct, targetType); } catch (Exception e) { String errorMsg = "cannot convert '" + bStruct.getType() + "' to type '" + targetType + "': " + e.getMessage(); handleTypeConversionError(ctx, sf, j, errorMsg); } } private static void convertArrayToJSON(Strand ctx, int[] operands, StackFrame sf) { int i = operands[0]; int j = operands[1]; BNewArray bArray = (BNewArray) sf.refRegs[i]; try { sf.refRegs[j] = JSONUtils.convertArrayToJSON(bArray); } catch (Exception e) { String errorMsg = "cannot convert '" + bArray.getType() + "' to type '" + BTypes.typeJSON + "': " + e.getMessage(); handleTypeConversionError(ctx, sf, j, errorMsg); } } private static void convertJSONToArray(Strand ctx, int[] operands, StackFrame sf) { int i = operands[0]; int cpIndex = operands[1]; int j = operands[2]; BArrayType targetType = (BArrayType) ((TypeRefCPEntry) sf.constPool[cpIndex]).getType(); BRefType<?> json = sf.refRegs[i]; if (json == null) { handleTypeConversionError(ctx, sf, j, BTypes.typeNull, targetType); return; } try { sf.refRegs[j] = JSONUtils.convertJSON(json, targetType); } catch (Exception e) { String errorMsg = "cannot convert '" + json.getType() + "' to type '" + targetType + "': " + e.getMessage(); handleTypeConversionError(ctx, sf, j, errorMsg); } } private static void convertMapToJSON(Strand ctx, int[] operands, StackFrame sf) { int i = operands[0]; int cpIndex = operands[1]; int j = operands[2]; BJSONType targetType = (BJSONType) ((TypeRefCPEntry) sf.constPool[cpIndex]).getType(); BMap<String, ?> bMap = (BMap<String, ?>) sf.refRegs[i]; try { sf.refRegs[j] = JSONUtils.convertMapToJSON((BMap<String, BValue>) bMap, targetType); } catch (Exception e) { String errorMsg = "cannot convert '" + bMap.getType() + "' to type '" + targetType + "': " + e.getMessage(); handleTypeConversionError(ctx, sf, j, errorMsg); } } private static void convertJSONToMap(Strand ctx, int[] operands, StackFrame sf) { int i = operands[0]; int cpIndex = operands[1]; int j = operands[2]; BMapType targetType = (BMapType) ((TypeRefCPEntry) sf.constPool[cpIndex]).getType(); BRefType<?> json = sf.refRegs[i]; if (json == null) { handleTypeConversionError(ctx, sf, j, BTypes.typeNull, targetType); return; } try { sf.refRegs[j] = JSONUtils.jsonToBMap(json, targetType); } catch (Exception e) { String errorMsg = "cannot convert '" + json.getType() + "' to type '" + targetType + "': " + e.getMessage(); handleTypeConversionError(ctx, sf, j, errorMsg); } } private static void convertMapToStruct(Strand ctx, int[] operands, StackFrame sf) { int i = operands[0]; int cpIndex = operands[1]; int j = operands[2]; TypeRefCPEntry typeRefCPEntry = (TypeRefCPEntry) sf.constPool[cpIndex]; BStructureType structType = (BStructureType) typeRefCPEntry.getType(); BMap<String, BValue> bMap = (BMap<String, BValue>) sf.refRegs[i]; try { sf.refRegs[j] = convertMapToStruct(ctx, bMap, structType); } catch (BallerinaException e) { sf.refRegs[j] = null; String errorMsg = "cannot convert '" + bMap.getType() + "' to type '" + structType + ": " + e.getMessage(); handleTypeConversionError(ctx, sf, j, errorMsg); } } private static BMap<String, BValue> convertMapToStruct(Strand ctx, BMap<String, BValue> bMap, BStructureType structType) { BMap<String, BValue> bStruct = new BMap<>(structType); StructureTypeInfo structInfo = (StructureTypeInfo) structType.getTypeInfo(); for (StructFieldInfo fieldInfo : structInfo.getFieldInfoEntries()) { String key = fieldInfo.getName(); BType fieldType = fieldInfo.getFieldType(); boolean containsField = bMap.hasKey(key); if (!containsField) { DefaultValueAttributeInfo defaultValAttrInfo = (DefaultValueAttributeInfo) getAttributeInfo( fieldInfo, AttributeInfo.Kind.DEFAULT_VALUE_ATTRIBUTE); if (defaultValAttrInfo != null) { switch (fieldType.getTag()) { case TypeTags.INT_TAG: bStruct.put(key, new BInteger(defaultValAttrInfo.getDefaultValue().getIntValue())); continue; case TypeTags.BYTE_TAG: bStruct.put(key, new BByte(defaultValAttrInfo.getDefaultValue().getByteValue())); continue; case TypeTags.FLOAT_TAG: bStruct.put(key, new BFloat(defaultValAttrInfo.getDefaultValue().getFloatValue())); continue; case TypeTags.DECIMAL_TAG: bStruct.put(key, new BDecimal(defaultValAttrInfo.getDefaultValue().getDecimalValue())); continue; case TypeTags.STRING_TAG: bStruct.put(key, new BString(defaultValAttrInfo.getDefaultValue().getStringValue())); continue; case TypeTags.BOOLEAN_TAG: bStruct.put(key, new BBoolean(defaultValAttrInfo.getDefaultValue().getBooleanValue())); continue; } } bStruct.put(key, fieldType.getZeroValue()); continue; } BValue mapVal = bMap.get(key); if (mapVal == null && BTypes.isValueType(fieldType)) { throw BLangExceptionHelper.getRuntimeException(RuntimeErrors.INCOMPATIBLE_FIELD_TYPE_FOR_CASTING, key, fieldType, null); } if (mapVal != null && mapVal.getType().getTag() == TypeTags.MAP_TAG) { bStruct.put(key, convertMap(ctx, (BMap<String, BValue>) mapVal, fieldType, key)); continue; } if (!checkCast(mapVal, fieldType, new ArrayList<TypePair>())) { throw BLangExceptionHelper.getRuntimeException(RuntimeErrors.INCOMPATIBLE_FIELD_TYPE_FOR_CASTING, key, fieldType, mapVal == null ? null : mapVal.getType()); } bStruct.put(key, mapVal); } return bStruct; } private static BValue convertMap(Strand ctx, BMap<String, BValue> mapValue, BType targetType, String key) { switch (targetType.getTag()) { case TypeTags.RECORD_TYPE_TAG: return convertMapToStruct(ctx, mapValue, (BStructureType) targetType); case TypeTags.JSON_TAG: return JSONUtils.convertMapToJSON(mapValue, (BJSONType) targetType); case TypeTags.UNION_TAG: for (BType memType : ((BUnionType) targetType).getMemberTypes()) { try { return convertMap(ctx, mapValue, memType, key); } catch (BallerinaException e) { // ignore conversion exception if thrown when the expected type is a union // type, to allow attempting conversion for other types } } break; default: if (checkCast(mapValue, targetType, new ArrayList<TypePair>())) { return mapValue; } } throw BLangExceptionHelper.getRuntimeException(RuntimeErrors.INCOMPATIBLE_FIELD_TYPE_FOR_CASTING, key, targetType, mapValue.getType()); } private static void convertJSONToStruct(Strand ctx, int[] operands, StackFrame sf) { int i = operands[0]; int cpIndex = operands[1]; int j = operands[2]; TypeRefCPEntry typeRefCPEntry = (TypeRefCPEntry) sf.constPool[cpIndex]; BRefType<?> bjson = sf.refRegs[i]; if (bjson == null) { handleTypeConversionError(ctx, sf, j, bjson != null ? bjson.getType() : BTypes.typeNull, typeRefCPEntry.getType()); return; } try { sf.refRegs[j] = JSONUtils.convertJSONToStruct(bjson, (BStructureType) typeRefCPEntry.getType()); } catch (Exception e) { String errorMsg = "cannot convert '" + TypeConstants.JSON_TNAME + "' to type '" + typeRefCPEntry.getType() + "': " + e.getMessage(); handleTypeConversionError(ctx, sf, j, errorMsg); } } private static void handleNullRefError(Strand strand) { strand.setError(BLangVMErrors.createNullRefException(strand)); handleError(strand); } public static void handleError(Strand strand) { StackFrame sf = strand.currentFrame; // TODO: Fix me int ip = sf.ip; ip--; ErrorTableEntry match = ErrorTableEntry.getMatch(sf.callableUnitInfo.getPackageInfo(), ip); if (match != null) { sf.ip = match.ipTarget; sf.refRegs[match.regIndex] = strand.getError(); strand.setError(null); } else if (strand.fp > 0) { // Stop the observation context before popping the stack frame ObserveUtils.stopCallableObservation(strand); StackFrame popedFrame = strand.popFrame(); popedFrame.handleChannelPanic(strand.getError(), strand.currentFrame.wdChannels); signalTransactionError(strand, popedFrame.trxParticipant); handleError(strand); } else { strand.respCallback.setError(strand.getError()); strand.currentFrame.handleChannelPanic(strand.getError(), strand.respCallback.parentChannels); signalTransactionError(strand, StackFrame.TransactionParticipantType.REMOTE_PARTICIPANT); //Below is to return current thread from VM sf.ip = -1; strand.respCallback.signal(); } } private static void signalTransactionError(Strand strand, StackFrame.TransactionParticipantType transactionParticipant) { TransactionLocalContext transactionLocalContext = strand.getLocalTransactionContext(); if (transactionLocalContext == null) { return; } boolean resourceParticipant = transactionLocalContext.isResourceParticipant(); if (resourceParticipant && transactionParticipant == StackFrame.TransactionParticipantType.REMOTE_PARTICIPANT) { transactionLocalContext.notifyLocalRemoteParticipantFailure(); } else if (transactionParticipant == StackFrame.TransactionParticipantType.LOCAL_PARTICIPANT) { transactionLocalContext.notifyLocalParticipantFailure(); } else if (strand.aborted) { String blockID = transactionLocalContext.getCurrentTransactionBlockId(); notifyTransactionAbort(strand, blockID, transactionLocalContext); } } private static AttributeInfo getAttributeInfo(AttributeInfoPool attrInfoPool, AttributeInfo.Kind attrInfoKind) { for (AttributeInfo attributeInfo : attrInfoPool.getAttributeInfoEntries()) { if (attributeInfo.getKind() == attrInfoKind) { return attributeInfo; } } return null; } private static boolean execWait(Strand strand, int[] operands) { int c = operands[0]; TypeRefCPEntry typeEntry = (TypeRefCPEntry) strand.currentFrame.constPool[operands[1]]; BType expType = typeEntry.getType(); int retValReg = operands[2]; SafeStrandCallback[] callbacks = new SafeStrandCallback[c]; for (int i = 0; i < c; i++) { int futureReg = operands[i + 3]; BFuture future = (BFuture) strand.currentFrame.refRegs[futureReg]; callbacks[i] = (SafeStrandCallback) future.value().respCallback; } strand.createWaitHandler(c, null); return WaitCallbackHandler.handleReturnInWait(strand, expType, retValReg, callbacks); } private static boolean execWaitForAll(Strand strand, int[] operands) { int c = operands[0]; // TODO: 11/22/18 Remove this from the CodeGen TypeRefCPEntry typeEntry = (TypeRefCPEntry) strand.currentFrame.constPool[operands[1]]; BType expType = typeEntry.getType(); int retValReg = operands[2]; List<SafeStrandCallback.WaitMultipleCallback> callbackList = new ArrayList<>(); for (int i = 0; i < c; i = i + 2) { int index = i + 3; // Get the key int keyRegIndex = operands[index]; // Get the expression followed int futureReg = operands[index + 1]; BFuture future = (BFuture) strand.currentFrame.refRegs[futureReg]; callbackList.add(new SafeStrandCallback.WaitMultipleCallback(keyRegIndex, (SafeStrandCallback) future.value().respCallback)); } strand.createWaitHandler(c, new ArrayList(callbackList.stream() .map(SafeStrandCallback.WaitMultipleCallback::getKeyRegIndex).collect(Collectors.toList()))); return WaitCallbackHandler.handleReturnInWaitMultiple(strand, retValReg, callbackList); } /** * This is used to propagate the results of {@link BVM#handleError(Strand)} to the * main CPU instruction loop. */ public static class HandleErrorException extends BallerinaException { private static final long serialVersionUID = 1L; public WorkerExecutionContext ctx; public HandleErrorException(WorkerExecutionContext ctx) { this.ctx = ctx; } } private static void handleMapStore(Strand ctx, BMap<String, BRefType> bMap, String fieldName, BRefType<?> value) { BType mapType = bMap.getType(); switch (mapType.getTag()) { case TypeTags.MAP_TAG: if (!isValidMapInsertion(mapType, value)) { BType expType = ((BMapType) mapType).getConstrainedType(); throw new BLangMapStoreException(BallerinaErrorReasons.INHERENT_TYPE_VIOLATION_ERROR, BLangExceptionHelper.getErrorMessage(RuntimeErrors.INVALID_MAP_INSERTION, expType, value.getType())); } insertToMap(ctx, bMap, fieldName, value); break; case TypeTags.OBJECT_TYPE_TAG: case TypeTags.SERVICE_TAG: BObjectType objType = (BObjectType) mapType; BField objField = objType.getFields().get(fieldName); BType objFieldType = objField.getFieldType(); if (!checkIsType(value, objFieldType)) { throw new BLangMapStoreException(BallerinaErrorReasons.INHERENT_TYPE_VIOLATION_ERROR, BLangExceptionHelper.getErrorMessage(RuntimeErrors.INVALID_OBJECT_FIELD_ADDITION, fieldName, objFieldType, value.getType())); } insertToMap(ctx, bMap, fieldName, value); break; case TypeTags.RECORD_TYPE_TAG: BRecordType recType = (BRecordType) mapType; BField recField = recType.getFields().get(fieldName); BType recFieldType; if (recField != null) { // If there is a corresponding field in the record, use it recFieldType = recField.fieldType; } else if (recType.restFieldType != null) { // If there isn't a corresponding field, but there is a rest field, use it recFieldType = recType.restFieldType; } else { // If both of the above conditions fail, the implication is that this is an attempt to insert a // value to a non-existent field in a closed record. throw new BLangMapStoreException(BallerinaErrorReasons.KEY_NOT_FOUND_ERROR, BLangExceptionHelper .getErrorMessage(RuntimeErrors.INVALID_RECORD_FIELD_ACCESS, fieldName, recType)); } if (!checkIsType(value, recFieldType)) { throw new BLangMapStoreException(BallerinaErrorReasons.INHERENT_TYPE_VIOLATION_ERROR, BLangExceptionHelper.getErrorMessage(RuntimeErrors.INVALID_RECORD_FIELD_ADDITION, fieldName, recFieldType, value.getType())); } insertToMap(ctx, bMap, fieldName, value); break; } } private static void insertToMap(Strand ctx, BMap bMap, String fieldName, BValue value) { try { bMap.put(fieldName, value); } catch (BLangFreezeException e) { // we would only reach here for record or map, not for object String errMessage = ""; switch (bMap.getType().getTag()) { case TypeTags.RECORD_TYPE_TAG: errMessage = "Invalid update of record field: "; break; case TypeTags.MAP_TAG: errMessage = "Invalid map insertion: "; break; } ctx.setError(BLangVMErrors.createError(ctx, e.getMessage(), errMessage + e.getDetail())); handleError(ctx); } } private static boolean isValidMapInsertion(BType mapType, BValue value) { if (value == null) { return true; } BType constraintType = ((BMapType) mapType).getConstrainedType(); if (constraintType == BTypes.typeAny || constraintType.equals(value.getType())) { return true; } return checkCast(value, constraintType, new ArrayList<>()); } public static boolean isAssignable(BType sourceType, BType targetType, List<TypePair> unresolvedTypes) { if (isSameOrAnyType(sourceType, targetType)) { return true; } if (targetType.getTag() == TypeTags.UNION_TAG) { return checkUnionAssignable(sourceType, targetType, unresolvedTypes); } // TODO: 6/26/18 complete impl. for JSON assignable if (targetType.getTag() == TypeTags.JSON_TAG && sourceType.getTag() == TypeTags.JSON_TAG) { return true; } if (targetType.getTag() == TypeTags.ARRAY_TAG && sourceType.getTag() == TypeTags.ARRAY_TAG) { if (((BArrayType) sourceType).getState() == BArrayState.CLOSED_SEALED && ((BArrayType) targetType).getState() == BArrayState.CLOSED_SEALED && ((BArrayType) sourceType).getSize() != ((BArrayType) targetType).getSize()) { return false; } return checkArrayCast(((BArrayType) sourceType).getElementType(), ((BArrayType) targetType).getElementType(), unresolvedTypes); } if (sourceType.getTag() == TypeTags.TUPLE_TAG && targetType.getTag() == TypeTags.TUPLE_TAG) { return checkTupleAssignable(sourceType, targetType, unresolvedTypes); } return checkCastByType(sourceType, targetType, unresolvedTypes); } private static boolean checkUnionAssignable(BType sourceType, BType targetType, List<TypePair> unresolvedTypes) { if (sourceType.getTag() == TypeTags.UNION_TAG) { for (BType sourceMemberType : ((BUnionType) sourceType).getMemberTypes()) { if (!checkUnionAssignable(sourceMemberType, targetType, unresolvedTypes)) { return false; } } return true; } else { BUnionType targetUnionType = (BUnionType) targetType; for (BType memberType : targetUnionType.getMemberTypes()) { if (isAssignable(sourceType, memberType, unresolvedTypes)) { return true; } } return false; } } private static boolean checkTupleAssignable(BType sourceType, BType targetType, List<TypePair> unresolvedTypes) { List<BType> targetTupleTypes = ((BTupleType) targetType).getTupleTypes(); List<BType> sourceTupleTypes = ((BTupleType) sourceType).getTupleTypes(); if (sourceTupleTypes.size() != targetTupleTypes.size()) { return false; } for (int i = 0; i < sourceTupleTypes.size(); i++) { if (!isAssignable(sourceTupleTypes.get(i), targetTupleTypes.get(i), unresolvedTypes)) { return false; } } return true; } private static boolean checkFunctionCast(BType sourceType, BFunctionType targetType) { if (sourceType.getTag() != TypeTags.FUNCTION_POINTER_TAG) { return false; } BFunctionType source = (BFunctionType) sourceType; if (source.paramTypes.length != targetType.paramTypes.length || source.retParamTypes.length != targetType.retParamTypes.length) { return false; } for (int i = 0; i < source.paramTypes.length; i++) { if (!isSameType(source.paramTypes[i], targetType.paramTypes[i])) { return false; } } for (int i = 0; i < source.retParamTypes.length; i++) { if (!isSameType(source.retParamTypes[i], targetType.retParamTypes[i])) { return false; } } return true; } private static boolean isDeepStampingRequiredForArray(BType sourceType) { BType elementType = ((BArrayType) sourceType).getElementType(); if (elementType != null) { if (BTypes.isValueType(elementType)) { return false; } else if (elementType instanceof BArrayType) { return isDeepStampingRequiredForArray(elementType); } return true; } return true; } private static boolean isDeepStampingRequiredForMap(BType sourceType) { BType constrainedType = ((BMapType) sourceType).getConstrainedType(); if (constrainedType != null) { if (BTypes.isValueType(constrainedType)) { return false; } else if (constrainedType instanceof BMapType) { return isDeepStampingRequiredForMap(constrainedType); } return true; } return true; } public static BType resolveMatchingTypeForUnion(BValue value, BType type) { if (value instanceof BValueArray && value.getType().getTag() == TypeTags.ARRAY_TAG && !isDeepStampingRequiredForArray(((BValueArray) value).getArrayType())) { return ((BValueArray) value).getArrayType(); } if (value instanceof BMap && value.getType().getTag() == TypeTags.MAP_TAG && !isDeepStampingRequiredForMap(value.getType())) { return value.getType(); } if (checkIsLikeType(value, BTypes.typeInt)) { return BTypes.typeInt; } if (checkIsLikeType(value, BTypes.typeFloat)) { return BTypes.typeFloat; } if (checkIsLikeType(value, BTypes.typeString)) { return BTypes.typeString; } if (checkIsLikeType(value, BTypes.typeBoolean)) { return BTypes.typeBoolean; } if (checkIsLikeType(value, BTypes.typeByte)) { return BTypes.typeByte; } BType anydataArrayType = new BArrayType(type); if (checkIsLikeType(value, anydataArrayType)) { return anydataArrayType; } if (checkIsLikeType(value, BTypes.typeXML)) { return BTypes.typeXML; } BType anydataMapType = new BMapType(type); if (checkIsLikeType(value, anydataMapType)) { return anydataMapType; } //not possible return null; } public static boolean checkIsLikeType(BValue sourceValue, BType targetType) { return checkIsLikeType(sourceValue, targetType, new ArrayList<>()); } public static boolean checkIsLikeType(BValue sourceValue, BType targetType, List<TypeValuePair> unresolvedValues) { BType sourceType = sourceValue == null ? BTypes.typeNull : sourceValue.getType(); if (checkIsType(sourceType, targetType, new ArrayList<>())) { return true; } switch (targetType.getTag()) { case TypeTags.RECORD_TYPE_TAG: return checkIsLikeRecordType(sourceValue, (BRecordType) targetType, unresolvedValues); case TypeTags.JSON_TAG: return checkIsLikeJSONType(sourceValue, (BJSONType) targetType, unresolvedValues); case TypeTags.MAP_TAG: return checkIsLikeMapType(sourceValue, (BMapType) targetType, unresolvedValues); case TypeTags.ARRAY_TAG: return checkIsLikeArrayType(sourceValue, (BArrayType) targetType, unresolvedValues); case TypeTags.TUPLE_TAG: return checkIsLikeTupleType(sourceValue, (BTupleType) targetType, unresolvedValues); case TypeTags.ERROR_TAG: return checkIsLikeErrorType(sourceValue, (BErrorType) targetType, unresolvedValues); case TypeTags.ANYDATA_TAG: return checkIsLikeAnydataType(sourceValue, unresolvedValues); case TypeTags.FINITE_TYPE_TAG: return checkFiniteTypeAssignable(sourceValue, targetType); case TypeTags.UNION_TAG: return ((BUnionType) targetType).getMemberTypes().stream() .anyMatch(type -> checkIsLikeType(sourceValue, type, unresolvedValues)); default: return false; } } private static boolean checkIsLikeAnydataType(BValue sourceValue, List<TypeValuePair> unresolvedValues) { switch (sourceValue.getType().getTag()) { case TypeTags.RECORD_TYPE_TAG: case TypeTags.JSON_TAG: case TypeTags.MAP_TAG: return ((BMap) sourceValue).getMap().values().stream() .allMatch(value -> checkIsLikeType((BValue) value, BTypes.typeAnydata, unresolvedValues)); case TypeTags.ARRAY_TAG: BNewArray arr = (BNewArray) sourceValue; BArrayType arrayType = (BArrayType) arr.getType(); switch (arrayType.getElementType().getTag()) { case TypeTags.INT_TAG: case TypeTags.FLOAT_TAG: case TypeTags.DECIMAL_TAG: case TypeTags.STRING_TAG: case TypeTags.BOOLEAN_TAG: case TypeTags.BYTE_TAG: return true; default: return Arrays.stream(((BValueArray) sourceValue).getValues()) .allMatch(value -> checkIsLikeType(value, BTypes.typeAnydata, unresolvedValues)); } case TypeTags.TUPLE_TAG: return Arrays.stream(((BValueArray) sourceValue).getValues()) .allMatch(value -> checkIsLikeType(value, BTypes.typeAnydata, unresolvedValues)); case TypeTags.ANYDATA_TAG: return true; case TypeTags.FINITE_TYPE_TAG: case TypeTags.UNION_TAG: return checkIsLikeType(sourceValue, BTypes.typeAnydata, unresolvedValues); default: return false; } } private static boolean checkIsLikeTupleType(BValue sourceValue, BTupleType targetType, List<TypeValuePair> unresolvedValues) { if (!(sourceValue instanceof BValueArray)) { return false; } BValueArray source = (BValueArray) sourceValue; if (source.size() != targetType.getTupleTypes().size()) { return false; } if (BTypes.isValueType(source.elementType)) { int bound = (int) source.size(); for (int i = 0; i < bound; i++) { if (!checkIsType(source.elementType, targetType.getTupleTypes().get(i), new ArrayList<>())) { return false; } } return true; } int bound = (int) source.size(); for (int i = 0; i < bound; i++) { if (!checkIsLikeType(source.getRefValue(i), targetType.getTupleTypes().get(i), unresolvedValues)) { return false; } } return true; } private static boolean checkIsLikeArrayType(BValue sourceValue, BArrayType targetType, List<TypeValuePair> unresolvedValues) { if (!(sourceValue instanceof BValueArray)) { return false; } BValueArray source = (BValueArray) sourceValue; if (BTypes.isValueType(source.elementType)) { return checkIsType(source.elementType, targetType.getElementType(), new ArrayList<>()); } BType arrayElementType = targetType.getElementType(); BRefType<?>[] arrayValues = source.getValues(); for (int i = 0; i < ((BValueArray) sourceValue).size(); i++) { if (!checkIsLikeType(arrayValues[i], arrayElementType, unresolvedValues)) { return false; } } return true; } private static boolean checkIsLikeMapType(BValue sourceValue, BMapType targetType, List<TypeValuePair> unresolvedValues) { if (!(sourceValue instanceof BMap)) { return false; } for (Object mapEntry : ((BMap) sourceValue).values()) { if (!checkIsLikeType((BValue) mapEntry, targetType.getConstrainedType(), unresolvedValues)) { return false; } } return true; } private static boolean checkIsLikeJSONType(BValue sourceValue, BJSONType targetType, List<TypeValuePair> unresolvedValues) { if (sourceValue.getType().getTag() == TypeTags.ARRAY_TAG) { BValueArray source = (BValueArray) sourceValue; if (BTypes.isValueType(source.elementType)) { return checkIsType(source.elementType, targetType, new ArrayList<>()); } BRefType<?>[] arrayValues = source.getValues(); for (int i = 0; i < ((BValueArray) sourceValue).size(); i++) { if (!checkIsLikeType(arrayValues[i], targetType, unresolvedValues)) { return false; } } return true; } else if (sourceValue.getType().getTag() == TypeTags.MAP_TAG) { for (BValue value : ((BMap) sourceValue).values()) { if (!checkIsLikeType(value, targetType, unresolvedValues)) { return false; } } return true; } else if (sourceValue.getType().getTag() == TypeTags.RECORD_TYPE_TAG) { TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); if (unresolvedValues.contains(typeValuePair)) { return true; } unresolvedValues.add(typeValuePair); for (Object object : ((BMap) sourceValue).getMap().values()) { if (!checkIsLikeType((BValue) object, targetType, unresolvedValues)) { return false; } } return true; } return false; } private static boolean checkIsLikeRecordType(BValue sourceValue, BRecordType targetType, List<TypeValuePair> unresolvedValues) { if (!(sourceValue instanceof BMap)) { return false; } TypeValuePair typeValuePair = new TypeValuePair(sourceValue, targetType); if (unresolvedValues.contains(typeValuePair)) { return true; } unresolvedValues.add(typeValuePair); Map<String, BType> targetTypeField = new HashMap<>(); BType restFieldType = targetType.restFieldType; for (BField field : targetType.getFields().values()) { targetTypeField.put(field.getFieldName(), field.fieldType); } for (Map.Entry targetTypeEntry : targetTypeField.entrySet()) { String fieldName = targetTypeEntry.getKey().toString(); if (!(((BMap) sourceValue).getMap().containsKey(fieldName)) && !Flags.isFlagOn(targetType.getFields().get(fieldName).flags, Flags.OPTIONAL)) { return false; } } for (Object object : ((BMap) sourceValue).getMap().entrySet()) { Map.Entry valueEntry = (Map.Entry) object; String fieldName = valueEntry.getKey().toString(); if (targetTypeField.containsKey(fieldName)) { if (!checkIsLikeType(((BValue) valueEntry.getValue()), targetTypeField.get(fieldName), unresolvedValues)) { return false; } } else { if (!targetType.sealed) { if (!checkIsLikeType(((BValue) valueEntry.getValue()), restFieldType, unresolvedValues)) { return false; } } else { return false; } } } return true; } private static boolean checkIsLikeErrorType(BValue sourceValue, BErrorType targetType, List<TypeValuePair> unresolvedValues) { if (sourceValue == null || sourceValue.getType().getTag() != TypeTags.ERROR_TAG) { return false; } return checkIsLikeType(new BString(((BError) sourceValue).reason), targetType.reasonType, unresolvedValues) && checkIsLikeType(((BError) sourceValue).details, targetType.detailsType, unresolvedValues); } public static boolean checkIsType(BValue sourceVal, BType targetType) { if (isMutable(sourceVal)) { BType sourceType = sourceVal == null ? BTypes.typeNull : sourceVal.getType(); return checkIsType(sourceType, targetType, new ArrayList<>()); } return checkIsLikeType(sourceVal, targetType, new ArrayList<>()); } private static boolean checkIsType(BType sourceType, BType targetType, List<TypePair> unresolvedTypes) { // First check whether both types are the same. if (sourceType == targetType || sourceType.equals(targetType)) { return true; } switch (targetType.getTag()) { case TypeTags.INT_TAG: case TypeTags.FLOAT_TAG: case TypeTags.DECIMAL_TAG: case TypeTags.STRING_TAG: case TypeTags.BOOLEAN_TAG: case TypeTags.BYTE_TAG: case TypeTags.NULL_TAG: case TypeTags.XML_TAG: case TypeTags.SERVICE_TAG: if (sourceType.getTag() == TypeTags.FINITE_TYPE_TAG) { return ((BFiniteType) sourceType).valueSpace.stream() .allMatch(bValue -> checkIsType(bValue, targetType)); } return sourceType.getTag() == targetType.getTag(); case TypeTags.MAP_TAG: return checkIsMapType(sourceType, (BMapType) targetType, unresolvedTypes); case TypeTags.JSON_TAG: return checkIsJSONType(sourceType, (BJSONType) targetType, unresolvedTypes); case TypeTags.RECORD_TYPE_TAG: return checkIsRecordType(sourceType, (BRecordType) targetType, unresolvedTypes); case TypeTags.FUNCTION_POINTER_TAG: return checkFunctionCast(sourceType, (BFunctionType) targetType); case TypeTags.ARRAY_TAG: return checkIsArrayType(sourceType, (BArrayType) targetType, unresolvedTypes); case TypeTags.TUPLE_TAG: return checkIsTupleType(sourceType, (BTupleType) targetType, unresolvedTypes); case TypeTags.UNION_TAG: return checkIsUnionType(sourceType, (BUnionType) targetType, unresolvedTypes); case TypeTags.TABLE_TAG: return checkIsTableType(sourceType, (BTableType) targetType, unresolvedTypes); case TypeTags.ANY_TAG: return checkIsAnyType(sourceType); case TypeTags.ANYDATA_TAG: case TypeTags.OBJECT_TYPE_TAG: return isAssignable(sourceType, targetType, unresolvedTypes); case TypeTags.FINITE_TYPE_TAG: return checkIsFiniteType(sourceType, (BFiniteType) targetType, unresolvedTypes); case TypeTags.FUTURE_TAG: return checkIsFutureType(sourceType, (BFutureType) targetType, unresolvedTypes); case TypeTags.STREAM_TAG: return checkIsStreamType(sourceType, (BStreamType) targetType, unresolvedTypes); default: return false; } } private static boolean checkIsMapType(BType sourceType, BMapType targetType, List<TypePair> unresolvedTypes) { if (sourceType.getTag() != TypeTags.MAP_TAG) { return false; } return checkContraints(((BMapType) sourceType).getConstrainedType(), targetType.getConstrainedType(), unresolvedTypes); } private static boolean checkIsJSONType(BType sourceType, BJSONType targetType, List<TypePair> unresolvedTypes) { switch (sourceType.getTag()) { case TypeTags.STRING_TAG: case TypeTags.INT_TAG: case TypeTags.FLOAT_TAG: case TypeTags.DECIMAL_TAG: case TypeTags.BOOLEAN_TAG: case TypeTags.NULL_TAG: case TypeTags.JSON_TAG: return true; case TypeTags.ARRAY_TAG: // Element type of the array should be 'is type' JSON return checkIsType(((BArrayType) sourceType).getElementType(), targetType, unresolvedTypes); case TypeTags.MAP_TAG: return checkCastByType(((BMapType) sourceType).getConstrainedType(), targetType, unresolvedTypes); default: return false; } } private static boolean checkIsRecordType(BType sourceType, BRecordType targetType, List<TypePair> unresolvedTypes) { if (sourceType.getTag() != TypeTags.RECORD_TYPE_TAG) { return false; } // If we encounter two types that we are still resolving, then skip it. // This is done to avoid recursive checking of the same type. TypePair pair = new TypePair(sourceType, targetType); if (unresolvedTypes.contains(pair)) { return true; } unresolvedTypes.add(pair); // Unsealed records are not equivalent to sealed records. But vice-versa is allowed. BRecordType sourceRecordType = (BRecordType) sourceType; if (targetType.sealed && !sourceRecordType.sealed) { return false; } // If both are sealed (one is sealed means other is also sealed) check the rest field type if (!sourceRecordType.sealed && !checkIsType(sourceRecordType.restFieldType, targetType.restFieldType, unresolvedTypes)) { return false; } Map<String, BField> sourceFields = sourceRecordType.getFields(); Set<String> targetFieldNames = targetType.getFields().keySet(); for (BField targetField : targetType.getFields().values()) { BField sourceField = sourceFields.get(targetField.getFieldName()); // If the LHS field is a required one, there has to be a corresponding required field in the RHS record. if (!Flags.isFlagOn(targetField.flags, Flags.OPTIONAL) && (sourceField == null || Flags.isFlagOn(sourceField.flags, Flags.OPTIONAL))) { return false; } if (sourceField == null || !checkIsType(sourceField.fieldType, targetField.fieldType, unresolvedTypes)) { return false; } } // If there are fields remaining in the source record, first check if it's a closed record. Closed records // should only have the fields specified by its type. if (targetType.sealed) { return targetFieldNames.containsAll(sourceFields.keySet()); } // If it's an open record, check if they are compatible with the rest field of the target type. return sourceFields.values().stream().filter(field -> !targetFieldNames.contains(field.fieldName)) .allMatch(field -> checkIsType(field.getFieldType(), targetType.restFieldType, unresolvedTypes)); } private static boolean checkIsUnionType(BType sourceType, BUnionType targetType, List<TypePair> unresolvedTypes) { if (sourceType.getTag() == TypeTags.UNION_TAG) { return ((BUnionType) sourceType).getMemberTypes().stream() .allMatch(type -> checkIsType(type, targetType, unresolvedTypes)); } else if (sourceType.getTag() == TypeTags.FINITE_TYPE_TAG) { return ((BFiniteType) sourceType).valueSpace.stream() .allMatch(bValue -> checkIsType(bValue, targetType)); } return targetType.getMemberTypes().stream() .anyMatch(type -> checkIsType(sourceType, type, unresolvedTypes)); } private static boolean checkIsTableType(BType sourceType, BTableType targetType, List<TypePair> unresolvedTypes) { if (sourceType.getTag() != TypeTags.TABLE_TAG) { return false; } return checkTableConstraints(((BTableType) sourceType).getConstrainedType(), targetType.getConstrainedType(), unresolvedTypes); } private static boolean checkIsAnyType(BType sourceType) { switch (sourceType.getTag()) { case TypeTags.ERROR_TAG: return false; case TypeTags.UNION_TAG: return ((BUnionType) sourceType).getMemberTypes().stream().allMatch(BVM::checkIsAnyType); } return true; } private static boolean checkIsArrayType(BType sourceType, BArrayType targetType, List<TypePair> unresolvedTypes) { if (sourceType.getTag() != TypeTags.ARRAY_TAG) { return false; } BArrayType sourceArrayType = (BArrayType) sourceType; if (sourceArrayType.getState() != targetType.getState() || sourceArrayType.getSize() != targetType.getSize()) { return false; } return checkIsType(sourceArrayType.getElementType(), targetType.getElementType(), unresolvedTypes); } private static boolean checkIsTupleType(BType sourceType, BTupleType targetType, List<TypePair> unresolvedTypes) { if (sourceType.getTag() != TypeTags.TUPLE_TAG) { return false; } List<BType> sourceTypes = ((BTupleType) sourceType).getTupleTypes(); List<BType> targetTypes = targetType.getTupleTypes(); if (sourceTypes.size() != targetTypes.size()) { return false; } for (int i = 0; i < sourceTypes.size(); i++) { if (!checkIsType(sourceTypes.get(i), targetTypes.get(i), unresolvedTypes)) { return false; } } return true; } private static boolean checkIsFiniteType(BType sourceType, BFiniteType targetType, List<TypePair> unresolvedTypes) { if (sourceType.getTag() != TypeTags.FINITE_TYPE_TAG) { return false; } BFiniteType sourceFiniteType = (BFiniteType) sourceType; if (sourceFiniteType.valueSpace.size() != targetType.valueSpace.size()) { return false; } return sourceFiniteType.valueSpace.stream().allMatch(value -> targetType.valueSpace.contains(value)); } private static boolean checkIsFutureType(BType sourceType, BFutureType targetType, List<TypePair> unresolvedTypes) { if (sourceType.getTag() != TypeTags.FUTURE_TAG) { return false; } return checkContraints(((BFutureType) sourceType).getConstrainedType(), targetType.getConstrainedType(), unresolvedTypes); } private static boolean checkIsStreamType(BType sourceType, BStreamType targetType, List<TypePair> unresolvedTypes) { if (sourceType.getTag() != TypeTags.STREAM_TAG) { return false; } return checkContraints(((BStreamType) sourceType).getConstrainedType(), targetType.getConstrainedType(), unresolvedTypes); } private static boolean checkContraints(BType sourceConstraint, BType targetConstraint, List<TypePair> unresolvedTypes) { if (sourceConstraint == null) { sourceConstraint = BTypes.typeAny; } if (targetConstraint == null) { targetConstraint = BTypes.typeAny; } return checkIsType(sourceConstraint, targetConstraint, unresolvedTypes); } private static boolean checkTableConstraints(BType sourceConstraint, BType targetConstraint, List<TypePair> unresolvedTypes) { // handle unconstrained tables returned by actions if (sourceConstraint == null) { if (targetConstraint.getTag() == TypeTags.RECORD_TYPE_TAG) { BRecordType targetConstrRecord = (BRecordType) targetConstraint; return !targetConstrRecord.sealed && targetConstrRecord.restFieldType == BTypes.typeAnydata; } return false; } return checkIsType(sourceConstraint, targetConstraint, unresolvedTypes); } private static boolean isMutable(BValue value) { if (value == null) { return false; } // All the value types are immutable if (value.getType().getTag() < TypeTags.JSON_TAG || value.getType().getTag() == TypeTags.FINITE_TYPE_TAG || value.getType().getTag() == TypeTags.ERROR_TAG) { // to be removed once error is frozen return false; } return !value.isFrozen(); } /** * Deep value equality check for anydata. * * @param lhsValue The value on the left hand side * @param rhsValue The value on the right hand side * @param checkedValues Structured value pairs already compared or being compared * @return True if values are equal, else false. */ private static boolean isEqual(BValue lhsValue, BValue rhsValue, List<ValuePair> checkedValues) { if (lhsValue == rhsValue) { return true; } if (null == lhsValue || null == rhsValue) { return false; } int lhsValTypeTag = lhsValue.getType().getTag(); int rhsValTypeTag = rhsValue.getType().getTag(); switch (lhsValTypeTag) { case TypeTags.STRING_TAG: case TypeTags.FLOAT_TAG: case TypeTags.DECIMAL_TAG: case TypeTags.BOOLEAN_TAG: return lhsValue.equals(rhsValue); case TypeTags.INT_TAG: if (rhsValTypeTag == TypeTags.BYTE_TAG) { return ((BInteger) lhsValue).intValue() == (((BByte) rhsValue).intValue()); } return lhsValue.equals(rhsValue); case TypeTags.BYTE_TAG: if (rhsValTypeTag == TypeTags.INT_TAG) { return ((BByte) lhsValue).intValue() == (((BInteger) rhsValue).intValue()); } return lhsValue.equals(rhsValue); case TypeTags.XML_TAG: return XMLUtils.isEqual((BXML) lhsValue, (BXML) rhsValue); case TypeTags.TABLE_TAG: // TODO: 10/8/18 break; case TypeTags.MAP_TAG: case TypeTags.JSON_TAG: case TypeTags.RECORD_TYPE_TAG: return isMappingType(rhsValTypeTag) && isEqual((BMap) lhsValue, (BMap) rhsValue, checkedValues); case TypeTags.TUPLE_TAG: case TypeTags.ARRAY_TAG: return isListType(rhsValTypeTag) && isEqual((BNewArray) lhsValue, (BNewArray) rhsValue, checkedValues); case TypeTags.SERVICE_TAG: break; } return false; } private static boolean isListType(int typeTag) { return typeTag == TypeTags.ARRAY_TAG || typeTag == TypeTags.TUPLE_TAG; } private static boolean isMappingType(int typeTag) { return typeTag == TypeTags.MAP_TAG || typeTag == TypeTags.RECORD_TYPE_TAG || typeTag == TypeTags.JSON_TAG; } /** * Deep equality check for an array/tuple. * * @param lhsList The array/tuple on the left hand side * @param rhsList The array/tuple on the right hand side * @param checkedValues Structured value pairs already compared or being compared * @return True if the array/tuple values are equal, else false. */ private static boolean isEqual(BNewArray lhsList, BNewArray rhsList, List<ValuePair> checkedValues) { ValuePair compValuePair = new ValuePair(lhsList, rhsList); if (checkedValues.contains(compValuePair)) { return true; } checkedValues.add(compValuePair); if (lhsList.size() != rhsList.size()) { return false; } for (int i = 0; i < lhsList.size(); i++) { if (!isEqual(lhsList.getBValue(i), rhsList.getBValue(i), checkedValues)) { return false; } } return true; } /** * Deep equality check for a map. * * @param lhsMap Map on the left hand side * @param rhsMap Map on the right hand side * @param checkedValues Structured value pairs already compared or being compared * @return True if the map values are equal, else false. */ private static boolean isEqual(BMap lhsMap, BMap rhsMap, List<ValuePair> checkedValues) { ValuePair compValuePair = new ValuePair(lhsMap, rhsMap); if (checkedValues.contains(compValuePair)) { return true; } checkedValues.add(compValuePair); if (lhsMap.size() != rhsMap.size()) { return false; } if (!lhsMap.getMap().keySet().containsAll(rhsMap.getMap().keySet())) { return false; } Iterator<Map.Entry<String, BValue>> mapIterator = lhsMap.getMap().entrySet().iterator(); while (mapIterator.hasNext()) { Map.Entry<String, BValue> lhsMapEntry = mapIterator.next(); if (!isEqual(lhsMapEntry.getValue(), rhsMap.get(lhsMapEntry.getKey()), checkedValues)) { return false; } } return true; } /** * Maintains the frozen status of a freezable {@link BValue}. * * @since 0.985.0 */ public static class FreezeStatus { /** * Representation of the current state of a freeze attempt. */ public enum State { FROZEN, MID_FREEZE, UNFROZEN; } private State currentState; public FreezeStatus(State state) { this.currentState = state; } public void setFrozen() { this.currentState = State.FROZEN; } public void setUnfrozen() { this.currentState = State.UNFROZEN; } public State getState() { return currentState; } public boolean isFrozen() { return currentState == State.FROZEN; } } /** * Reference equality check for values. If both the values are simple basic types, returns the same * result as {@link BVM#isEqual(BValue, BValue, List)} * * @param lhsValue The value on the left hand side * @param rhsValue The value on the right hand side * @return True if values are reference equal or in the case of simple basic types if the values are equal, * else false. */ private static boolean isReferenceEqual(BValue lhsValue, BValue rhsValue) { if (lhsValue == rhsValue) { return true; } // if one is null, the other also needs to be null to be true if (lhsValue == null || rhsValue == null) { return false; } if (isSimpleBasicType(lhsValue.getType()) && isSimpleBasicType(rhsValue.getType())) { return isEqual(lhsValue, rhsValue, Collections.emptyList()); } return false; } /** * Reference inequality check for values. If both the values are simple basic types, returns the same * result as the negation of {@link BVM#isEqual(BValue, BValue, List)} * * @param lhsValue The value on the left hand side * @param rhsValue The value on the right hand side * @return True if values are not reference equal or in the case of simple basic types if the values are not equal, * else false. */ private static boolean isReferenceInequal(BValue lhsValue, BValue rhsValue) { if (lhsValue == null || rhsValue == null) { return lhsValue != rhsValue; } if (isSimpleBasicType(lhsValue.getType()) && isSimpleBasicType(rhsValue.getType())) { return !isEqual(lhsValue, rhsValue, Collections.emptyList()); } return lhsValue != rhsValue; } private static boolean isSimpleBasicType(BType type) { return type.getTag() < TypeTags.JSON_TAG; } private static boolean isBasicNumericType(BType type) { return type.getTag() < TypeTags.STRING_TAG; } private static boolean containsNumericType(BType type) { if (type.getTag() == TypeTags.UNION_TAG) { return ((BUnionType) type).getMemberTypes().stream().anyMatch(BVM::containsNumericType); } return isBasicNumericType(type); } /** * Type vector of size two, to hold the source and the target types. * * @since 0.982.0 */ private static class TypePair { BType sourceType; BType targetType; public TypePair(BType sourceType, BType targetType) { this.sourceType = sourceType; this.targetType = targetType; } @Override public boolean equals(Object obj) { if (!(obj instanceof TypePair)) { return false; } TypePair other = (TypePair) obj; return this.sourceType.equals(other.sourceType) && this.targetType.equals(other.targetType); } } /** * Unordered BValue vector of size two, to hold two values being compared. * * @since 0.985.0 */ private static class ValuePair { List<BValue> valueList = new ArrayList<>(2); ValuePair(BValue valueOne, BValue valueTwo) { valueList.add(valueOne); valueList.add(valueTwo); } @Override public boolean equals(Object otherPair) { if (!(otherPair instanceof ValuePair)) { return false; } return ((ValuePair) otherPair).valueList.containsAll(valueList) && valueList.containsAll(((ValuePair) otherPair).valueList); } } /** * Type vector of size two, to hold the source value and the target type. * * @since 0.990.3 */ public static class TypeValuePair { BValue sourceValue; BType targetType; public TypeValuePair(BValue sourceValue, BType targetType) { this.sourceValue = sourceValue; this.targetType = targetType; } @Override public boolean equals(Object obj) { if (!(obj instanceof TypeValuePair)) { return false; } TypeValuePair other = (TypeValuePair) obj; return this.sourceValue.equals(other.sourceValue) && this.targetType.equals(other.targetType); } } }