本文整理汇总了Golang中github.com/axw/gollvm/llvm.Value.IsNil方法的典型用法代码示例。如果您正苦于以下问题:Golang Value.IsNil方法的具体用法?Golang Value.IsNil怎么用?Golang Value.IsNil使用的例子?那么恭喜您, 这里精选的方法代码示例或许可以为您提供帮助。您也可以进一步了解该方法所在类github.com/axw/gollvm/llvm.Value
的用法示例。
在下文中一共展示了Value.IsNil方法的10个代码示例,这些例子默认根据受欢迎程度排序。您可以为喜欢或者感觉有用的代码点赞,您的评价将有助于系统推荐出更棒的Golang代码示例。
示例1: makeAlgorithmTable
func (tm *TypeMap) makeAlgorithmTable(t types.Type) llvm.Value {
// TODO set these to actual functions.
hashAlg := llvm.ConstNull(llvm.PointerType(tm.hashAlgFunctionType, 0))
printAlg := llvm.ConstNull(llvm.PointerType(tm.printAlgFunctionType, 0))
copyAlg := llvm.ConstNull(llvm.PointerType(tm.copyAlgFunctionType, 0))
const eqalgsig = "func(uintptr, unsafe.Pointer, unsafe.Pointer) bool"
var equalAlg llvm.Value
switch t := t.(type) {
case *types.Basic:
switch t.Kind() {
case types.String:
equalAlg = tm.functions.NamedFunction("runtime.streqalg", eqalgsig)
case types.Float32:
equalAlg = tm.functions.NamedFunction("runtime.f32eqalg", eqalgsig)
case types.Float64:
equalAlg = tm.functions.NamedFunction("runtime.f64eqalg", eqalgsig)
case types.Complex64:
equalAlg = tm.functions.NamedFunction("runtime.c64eqalg", eqalgsig)
case types.Complex128:
equalAlg = tm.functions.NamedFunction("runtime.c128eqalg", eqalgsig)
}
}
if equalAlg.IsNil() {
equalAlg = tm.functions.NamedFunction("runtime.memequal", eqalgsig)
}
elems := []llvm.Value{hashAlg, equalAlg, printAlg, copyAlg}
return llvm.ConstStruct(elems, false)
}
示例2: makeFunc
func (c *compiler) makeFunc(ident *ast.Ident, ftyp *types.Signature) *LLVMValue {
fname := ident.String()
if ftyp.Recv() == nil && fname == "init" {
// Make "init" functions anonymous.
fname = ""
} else {
var pkgname string
if recv := ftyp.Recv(); recv != nil {
var recvname string
switch recvtyp := recv.Type().(type) {
case *types.Pointer:
if named, ok := recvtyp.Elem().(*types.Named); ok {
obj := named.Obj()
recvname = "*" + obj.Name()
pkgname = obj.Pkg().Path()
}
case *types.Named:
named := recvtyp
obj := named.Obj()
recvname = obj.Name()
pkgname = obj.Pkg().Path()
}
if recvname != "" {
fname = fmt.Sprintf("%s.%s", recvname, fname)
} else {
// If the receiver is an unnamed struct, we're
// synthesising a method for an unnamed struct
// type. There's no meaningful name to give the
// function, so leave it up to LLVM.
fname = ""
}
} else {
obj := c.typeinfo.Objects[ident]
pkgname = obj.Pkg().Path()
}
if fname != "" {
fname = pkgname + "." + fname
}
}
// gcimporter may produce multiple AST objects for the same function.
llvmftyp := c.types.ToLLVM(ftyp)
var fn llvm.Value
if fname != "" {
fn = c.module.Module.NamedFunction(fname)
}
if fn.IsNil() {
llvmfptrtyp := llvmftyp.StructElementTypes()[0].ElementType()
fn = llvm.AddFunction(c.module.Module, fname, llvmfptrtyp)
}
fn = llvm.ConstInsertValue(llvm.ConstNull(llvmftyp), fn, []uint32{0})
return c.NewValue(fn, ftyp)
}
示例3: newStackVarEx
func (c *compiler) newStackVarEx(argument int, stackf *LLVMValue, v types.Object, value llvm.Value, name string) (stackvalue llvm.Value, stackvar *LLVMValue) {
typ := v.Type()
// We need to put alloca instructions in the top block or the values
// displayed when inspecting these variables in a debugger will be
// completely messed up.
curBlock := c.builder.GetInsertBlock()
if p := curBlock.Parent(); !p.IsNil() {
fb := p.FirstBasicBlock()
fi := fb.FirstInstruction()
if !fb.IsNil() && !fi.IsNil() {
c.builder.SetInsertPointBefore(fi)
}
}
old := c.builder.CurrentDebugLocation()
c.builder.SetCurrentDebugLocation(llvm.Value{})
stackvalue = c.builder.CreateAlloca(c.types.ToLLVM(typ), name)
// For arguments we want to insert the store instruction
// without debug information to ensure that they are executed
// (and hence have proper values) before the debugger hits the
// first line in a function.
if argument == 0 {
c.builder.SetCurrentDebugLocation(old)
c.builder.SetInsertPointAtEnd(curBlock)
}
if !value.IsNil() {
c.builder.CreateStore(value, stackvalue)
}
c.builder.SetCurrentDebugLocation(old)
c.builder.SetInsertPointAtEnd(curBlock)
ptrvalue := c.NewValue(stackvalue, types.NewPointer(typ))
stackvar = ptrvalue.makePointee()
stackvar.stack = stackf
c.objectdata[v].Value = stackvar
file := c.fileset.File(v.Pos())
tag := llvm.DW_TAG_auto_variable
if argument > 0 {
tag = llvm.DW_TAG_arg_variable
}
ld := llvm.NewLocalVariableDescriptor(tag)
ld.Argument = uint32(argument)
ld.Line = uint32(file.Line(v.Pos()))
ld.Name = name
ld.File = &llvm.ContextDescriptor{llvm.FileDescriptor(file.Name())}
ld.Type = c.tollvmDebugDescriptor(typ)
ld.Context = c.currentDebugContext()
c.builder.InsertDeclare(c.module.Module, llvm.MDNode([]llvm.Value{stackvalue}), c.debug_info.MDNode(ld))
return stackvalue, stackvar
}
示例4: newStackVarEx
func (c *compiler) newStackVarEx(argument int, stackf *LLVMValue, v types.Object, value llvm.Value, name string) (stackvalue llvm.Value, stackvar *LLVMValue) {
typ := v.Type()
// We need to put alloca instructions in the top block or the values
// displayed when inspecting these variables in a debugger will be
// completely messed up.
curBlock := c.builder.GetInsertBlock()
if p := curBlock.Parent(); !p.IsNil() {
fb := p.FirstBasicBlock()
fi := fb.FirstInstruction()
if !fb.IsNil() && !fi.IsNil() {
c.builder.SetInsertPointBefore(fi)
}
}
old := c.builder.CurrentDebugLocation()
c.builder.SetCurrentDebugLocation(llvm.Value{})
stackvalue = c.builder.CreateAlloca(c.types.ToLLVM(typ), name)
// For arguments we want to insert the store instruction
// without debug information to ensure that they are executed
// (and hence have proper values) before the debugger hits the
// first line in a function.
if argument == 0 {
c.builder.SetCurrentDebugLocation(old)
c.builder.SetInsertPointAtEnd(curBlock)
}
if !value.IsNil() {
c.builder.CreateStore(value, stackvalue)
}
c.builder.SetCurrentDebugLocation(old)
c.builder.SetInsertPointAtEnd(curBlock)
ptrvalue := c.NewValue(stackvalue, types.NewPointer(typ))
stackvar = ptrvalue.makePointee()
stackvar.stack = stackf
c.objectdata[v].Value = stackvar
// Generate debug metadata (will return nil
// if debug-data generation is disabled).
if descriptor := c.createLocalVariableMetadata(v, argument); descriptor != nil {
c.builder.InsertDeclare(
c.module.Module,
llvm.MDNode([]llvm.Value{stackvalue}),
c.debug_info.MDNode(descriptor),
)
}
return stackvalue, stackvar
}
示例5: chanSend
func (v *LLVMValue) chanSend(value Value) {
var ptr llvm.Value
if value, ok := value.(*LLVMValue); ok && value.pointer != nil {
ptr = value.pointer.LLVMValue()
}
elttyp := types.Underlying(v.typ).(*types.Chan).Elt
c := v.compiler
if ptr.IsNil() {
ptr = c.builder.CreateAlloca(c.types.ToLLVM(elttyp), "")
value := value.Convert(elttyp).LLVMValue()
c.builder.CreateStore(value, ptr)
}
uintptr_ := c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "")
f := c.NamedFunction("runtime.chansend", "func f(c, ptr uintptr)")
c.builder.CreateCall(f, []llvm.Value{v.LLVMValue(), uintptr_}, "")
}
示例6: chanSend
func (v *LLVMValue) chanSend(value Value) {
var ptr llvm.Value
if value, ok := value.(*LLVMValue); ok && value.pointer != nil {
ptr = value.pointer.LLVMValue()
}
elttyp := v.typ.Underlying().(*types.Chan).Elem()
c := v.compiler
if ptr.IsNil() {
ptr = c.builder.CreateAlloca(c.types.ToLLVM(elttyp), "")
value := value.Convert(elttyp).LLVMValue()
c.builder.CreateStore(value, ptr)
}
uintptr_ := c.builder.CreatePtrToInt(ptr, c.target.IntPtrType(), "")
nb := boolLLVMValue(false)
f := c.NamedFunction("runtime.chansend", "func(t *chanType, c, ptr uintptr, nb bool) bool")
chantyp := c.types.ToRuntime(v.typ.Underlying())
chantyp = c.builder.CreateBitCast(chantyp, f.Type().ElementType().ParamTypes()[0], "")
c.builder.CreateCall(f, []llvm.Value{chantyp, v.LLVMValue(), uintptr_, nb}, "")
// Ignore result; only used in runtime.
}
示例7: VisitSelectStmt
func (c *compiler) VisitSelectStmt(stmt *ast.SelectStmt) {
// TODO optimisations:
// 1. No clauses: runtime.block.
// 2. Single recv, and default clause: runtime.selectnbrecv
// 2. Single send, and default clause: runtime.selectnbsend
startBlock := c.builder.GetInsertBlock()
function := startBlock.Parent()
endBlock := llvm.AddBasicBlock(function, "end")
endBlock.MoveAfter(startBlock)
defer c.builder.SetInsertPointAtEnd(endBlock)
// Cache runtime functions
var selectrecv, selectsend llvm.Value
getselectsend := func() llvm.Value {
if selectsend.IsNil() {
selectsend = c.NamedFunction("runtime.selectsend", "func(selectp, blockaddr, ch, elem unsafe.Pointer)")
}
return selectsend
}
getselectrecv := func() llvm.Value {
if selectrecv.IsNil() {
selectrecv = c.NamedFunction("runtime.selectrecv", "func(selectp, blockaddr, ch, elem unsafe.Pointer, received *bool)")
}
return selectrecv
}
// We create a pointer-pointer for each newly defined var on the
// lhs of receive expressions, which will be assigned to when the
// expressions are (conditionally) evaluated.
lhsptrs := make([][]llvm.Value, len(stmt.Body.List))
for i, stmt := range stmt.Body.List {
clause := stmt.(*ast.CommClause)
if stmt, ok := clause.Comm.(*ast.AssignStmt); ok && stmt.Tok == token.DEFINE {
lhs := make([]llvm.Value, len(stmt.Lhs))
for i, expr := range stmt.Lhs {
ident := expr.(*ast.Ident)
if !isBlank(ident.Name) {
typ := c.target.IntPtrType()
lhs[i] = c.builder.CreateAlloca(typ, "")
c.builder.CreateStore(llvm.ConstNull(typ), lhs[i])
}
}
lhsptrs[i] = lhs
}
}
// Create clause basic blocks.
blocks := make([]llvm.BasicBlock, len(stmt.Body.List))
var basesize uint64
for i, stmt := range stmt.Body.List {
clause := stmt.(*ast.CommClause)
if clause.Comm == nil {
basesize++
}
currBlock := c.builder.GetInsertBlock()
block := llvm.InsertBasicBlock(endBlock, "")
c.builder.SetInsertPointAtEnd(block)
blocks[i] = block
lhs := lhsptrs[i]
if stmt, ok := clause.Comm.(*ast.AssignStmt); ok {
for i, expr := range stmt.Lhs {
ident := expr.(*ast.Ident)
if !isBlank(ident.Name) {
ptr := c.builder.CreateLoad(lhs[i], "")
obj := c.typeinfo.Objects[ident]
ptrtyp := types.NewPointer(obj.Type())
ptr = c.builder.CreateIntToPtr(ptr, c.types.ToLLVM(ptrtyp), "")
value := c.NewValue(ptr, ptrtyp).makePointee()
c.objectdata[obj].Value = value
}
}
}
for _, stmt := range clause.Body {
c.VisitStmt(stmt)
}
c.maybeImplicitBranch(endBlock)
c.builder.SetInsertPointAtEnd(currBlock)
}
// We need to make an initial pass through the cases,
// discarding those where the channel is nil.
size := llvm.ConstInt(llvm.Int32Type(), basesize, false)
channels := make([]Value, len(stmt.Body.List))
rhs := make([]*LLVMValue, len(stmt.Body.List))
for i, stmt := range stmt.Body.List {
clause := stmt.(*ast.CommClause)
switch comm := clause.Comm.(type) {
case nil:
case *ast.SendStmt:
channels[i] = c.VisitExpr(comm.Chan)
rhs[i] = c.VisitExpr(comm.Value).(*LLVMValue)
case *ast.ExprStmt:
channels[i] = c.VisitExpr(comm.X.(*ast.UnaryExpr).X)
case *ast.AssignStmt:
channels[i] = c.VisitExpr(comm.Rhs[0].(*ast.UnaryExpr).X)
default:
panic(fmt.Errorf("unhandled: %T", comm))
}
if channels[i] != nil {
//.........这里部分代码省略.........
示例8: VisitRangeStmt
//.........这里部分代码省略.........
case *types.Array:
isarray = true
x := x
if !isptr {
if x_, ok := x.(*LLVMValue); ok && x_.pointer != nil {
x = x_.pointer
} else {
ptr := c.builder.CreateAlloca(c.types.ToLLVM(x.Type()), "")
c.builder.CreateStore(x.LLVMValue(), ptr)
x = c.NewValue(ptr, types.NewPointer(x.Type()))
}
}
base = x.LLVMValue()
length = llvm.ConstInt(c.llvmtypes.inttype, uint64(typ.Len()), false)
goto arrayrange
case *types.Slice:
slicevalue := x.LLVMValue()
base = c.builder.CreateExtractValue(slicevalue, 0, "")
length = c.builder.CreateExtractValue(slicevalue, 1, "")
goto arrayrange
}
panic("unreachable")
maprange:
{
currBlock = c.builder.GetInsertBlock()
c.builder.CreateBr(condBlock)
c.builder.SetInsertPointAtEnd(condBlock)
nextptrphi := c.builder.CreatePHI(c.target.IntPtrType(), "next")
nextptr, pk, pv := c.mapNext(x.(*LLVMValue), nextptrphi)
notnull := c.builder.CreateIsNotNull(nextptr, "")
c.builder.CreateCondBr(notnull, loopBlock, doneBlock)
c.builder.SetInsertPointAtEnd(loopBlock)
if !keyPtr.IsNil() {
keyval := c.builder.CreateLoad(pk, "")
c.builder.CreateStore(keyval, keyPtr)
}
if !valuePtr.IsNil() {
valval := c.builder.CreateLoad(pv, "")
c.builder.CreateStore(valval, valuePtr)
}
c.VisitBlockStmt(stmt.Body, false)
c.maybeImplicitBranch(postBlock)
c.builder.SetInsertPointAtEnd(postBlock)
c.builder.CreateBr(condBlock)
nextptrphi.AddIncoming([]llvm.Value{llvm.ConstNull(c.target.IntPtrType()), nextptr}, []llvm.BasicBlock{currBlock, postBlock})
return
}
stringrange:
{
zero := llvm.ConstNull(c.types.inttype)
currBlock = c.builder.GetInsertBlock()
c.builder.CreateBr(condBlock)
c.builder.SetInsertPointAtEnd(condBlock)
index := c.builder.CreatePHI(c.types.inttype, "index")
lessthan := c.builder.CreateICmp(llvm.IntULT, index, length, "")
c.builder.CreateCondBr(lessthan, loopBlock, doneBlock)
c.builder.SetInsertPointAtEnd(loopBlock)
consumed, value := c.stringNext(x.LLVMValue(), index)
if !keyPtr.IsNil() {
c.builder.CreateStore(index, keyPtr)
}
if !valuePtr.IsNil() {
c.builder.CreateStore(value, valuePtr)
}
示例9: createCall
//.........这里部分代码省略.........
switch results := fn_type.Results(); results.Len() {
case 0: // no-op
case 1:
result_type = results.At(0).Type()
default:
result_type = results
}
// Depending on whether the function contains defer statements or not,
// we'll generate either a "call" or an "invoke" instruction.
var createCall = c.builder.CreateCall
if invoke {
f := c.functions.top()
// TODO Create a method on compiler (avoid creating closures).
createCall = func(fn llvm.Value, args []llvm.Value, name string) llvm.Value {
currblock := c.builder.GetInsertBlock()
returnblock := llvm.AddBasicBlock(currblock.Parent(), "")
returnblock.MoveAfter(currblock)
value := c.builder.CreateInvoke(fn, args, returnblock, f.unwindblock, "")
c.builder.SetInsertPointAtEnd(returnblock)
return value
}
}
var fnptr llvm.Value
fnval := fn.LLVMValue()
if fnval.Type().TypeKind() == llvm.PointerTypeKind {
fnptr = fnval
} else {
fnptr = c.builder.CreateExtractValue(fnval, 0, "")
context := c.builder.CreateExtractValue(fnval, 1, "")
fntyp := fnptr.Type().ElementType()
paramTypes := fntyp.ParamTypes()
// If the context is not a constant null, and we're not
// dealing with a method (where we don't care about the value
// of the receiver), then we must conditionally call the
// function with the additional receiver/closure.
if !context.IsNull() || fn_type.Recv() != nil {
// Store the blocks for referencing in the Phi below;
// note that we update the block after each createCall,
// since createCall may create new blocks and we want
// the predecessors to the Phi.
var nullctxblock llvm.BasicBlock
var nonnullctxblock llvm.BasicBlock
var endblock llvm.BasicBlock
var nullctxresult llvm.Value
// len(paramTypes) == len(args) iff function is not a method.
if !context.IsConstant() && len(paramTypes) == len(args) {
currblock := c.builder.GetInsertBlock()
endblock = llvm.AddBasicBlock(currblock.Parent(), "")
endblock.MoveAfter(currblock)
nonnullctxblock = llvm.InsertBasicBlock(endblock, "")
nullctxblock = llvm.InsertBasicBlock(nonnullctxblock, "")
nullctx := c.builder.CreateIsNull(context, "")
c.builder.CreateCondBr(nullctx, nullctxblock, nonnullctxblock)
// null context case.
c.builder.SetInsertPointAtEnd(nullctxblock)
nullctxresult = createCall(fnptr, args, "")
nullctxblock = c.builder.GetInsertBlock()
c.builder.CreateBr(endblock)
c.builder.SetInsertPointAtEnd(nonnullctxblock)
}
// non-null context case.
var result llvm.Value
args := append([]llvm.Value{context}, args...)
if len(paramTypes) < len(args) {
returnType := fntyp.ReturnType()
ctxType := context.Type()
paramTypes := append([]llvm.Type{ctxType}, paramTypes...)
vararg := fntyp.IsFunctionVarArg()
fntyp := llvm.FunctionType(returnType, paramTypes, vararg)
fnptrtyp := llvm.PointerType(fntyp, 0)
fnptr = c.builder.CreateBitCast(fnptr, fnptrtyp, "")
}
result = createCall(fnptr, args, "")
// If the return type is not void, create a
// PHI node to select which value to return.
if !nullctxresult.IsNil() {
nonnullctxblock = c.builder.GetInsertBlock()
c.builder.CreateBr(endblock)
c.builder.SetInsertPointAtEnd(endblock)
if result.Type().TypeKind() != llvm.VoidTypeKind {
phiresult := c.builder.CreatePHI(result.Type(), "")
values := []llvm.Value{nullctxresult, result}
blocks := []llvm.BasicBlock{nullctxblock, nonnullctxblock}
phiresult.AddIncoming(values, blocks)
result = phiresult
}
}
return c.NewValue(result, result_type)
}
}
result := createCall(fnptr, args, "")
return c.NewValue(result, result_type)
}
示例10: VisitRangeStmt
func (c *compiler) VisitRangeStmt(stmt *ast.RangeStmt) {
currBlock := c.builder.GetInsertBlock()
doneBlock := llvm.AddBasicBlock(currBlock.Parent(), "done")
doneBlock.MoveAfter(currBlock)
postBlock := llvm.InsertBasicBlock(doneBlock, "post")
loopBlock := llvm.InsertBasicBlock(postBlock, "loop")
condBlock := llvm.InsertBasicBlock(loopBlock, "cond")
defer c.builder.SetInsertPointAtEnd(doneBlock)
// Evaluate range expression first.
x := c.VisitExpr(stmt.X)
// If it's a pointer type, we'll first check that it's non-nil.
typ := types.Underlying(x.Type())
if _, ok := typ.(*types.Pointer); ok {
ifBlock := llvm.InsertBasicBlock(doneBlock, "if")
isnotnull := c.builder.CreateIsNotNull(x.LLVMValue(), "")
c.builder.CreateCondBr(isnotnull, ifBlock, doneBlock)
c.builder.SetInsertPointAtEnd(ifBlock)
}
// Is it a new var definition? Then allocate some memory on the stack.
var keyType, valueType types.Type
var keyPtr, valuePtr llvm.Value
if stmt.Tok == token.DEFINE {
if key := stmt.Key.(*ast.Ident); key.Name != "_" {
keyType = key.Obj.Type.(types.Type)
keyPtr = c.builder.CreateAlloca(c.types.ToLLVM(keyType), "")
key.Obj.Data = c.NewLLVMValue(keyPtr, &types.Pointer{Base: keyType}).makePointee()
}
if stmt.Value != nil {
if value := stmt.Value.(*ast.Ident); value.Name != "_" {
valueType = value.Obj.Type.(types.Type)
valuePtr = c.builder.CreateAlloca(c.types.ToLLVM(valueType), "")
value.Obj.Data = c.NewLLVMValue(valuePtr, &types.Pointer{Base: valueType}).makePointee()
}
}
}
c.breakblocks = append(c.breakblocks, doneBlock)
c.continueblocks = append(c.continueblocks, postBlock)
defer func() {
c.breakblocks = c.breakblocks[:len(c.breakblocks)-1]
c.continueblocks = c.continueblocks[:len(c.continueblocks)-1]
}()
isarray := false
var base, length llvm.Value
_, isptr := typ.(*types.Pointer)
if isptr {
typ = typ.(*types.Pointer).Base
}
switch typ := types.Underlying(typ).(type) {
case *types.Map:
goto maprange
case *types.Name:
stringvalue := x.LLVMValue()
length = c.builder.CreateExtractValue(stringvalue, 1, "")
goto stringrange
case *types.Array:
isarray = true
x := x
if !isptr {
if x_, ok := x.(*LLVMValue); ok && x_.pointer != nil {
x = x_.pointer
} else {
// TODO load value onto stack for indexing?
}
}
base = x.LLVMValue()
length = llvm.ConstInt(llvm.Int32Type(), typ.Len, false)
goto arrayrange
case *types.Slice:
slicevalue := x.LLVMValue()
base = c.builder.CreateExtractValue(slicevalue, 0, "")
length = c.builder.CreateExtractValue(slicevalue, 1, "")
goto arrayrange
}
maprange:
{
currBlock = c.builder.GetInsertBlock()
c.builder.CreateBr(condBlock)
c.builder.SetInsertPointAtEnd(condBlock)
nextptrphi := c.builder.CreatePHI(c.target.IntPtrType(), "next")
nextptr, pk, pv := c.mapNext(x.(*LLVMValue), nextptrphi)
notnull := c.builder.CreateIsNotNull(nextptr, "")
c.builder.CreateCondBr(notnull, loopBlock, doneBlock)
c.builder.SetInsertPointAtEnd(loopBlock)
if !keyPtr.IsNil() {
keyval := c.builder.CreateLoad(pk, "")
c.builder.CreateStore(keyval, keyPtr)
}
if !valuePtr.IsNil() {
valval := c.builder.CreateLoad(pv, "")
c.builder.CreateStore(valval, valuePtr)
}
c.VisitBlockStmt(stmt.Body, false)
c.maybeImplicitBranch(postBlock)
c.builder.SetInsertPointAtEnd(postBlock)
//.........这里部分代码省略.........